command_tower 0.17.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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +8 -8
  3. data/app/controllers/command_tower/me/inbox_controller.rb +16 -0
  4. data/app/deserializers/command_tower/deserializers/messaging/inbox.rb +85 -0
  5. data/app/jobs/command_tower/messaging/communications/produce_recipient_job.rb +2 -0
  6. data/app/models/command_tower/messaging/communication.rb +31 -0
  7. data/app/serializers/command_tower/serializers/messaging/inbox.rb +43 -0
  8. data/app/services/command_tower/messaging/accept/coordinator.rb +16 -1
  9. data/app/services/command_tower/messaging/accept/persister.rb +13 -0
  10. data/app/services/command_tower/messaging/contract/mappers/communication_mapper.rb +7 -0
  11. data/app/services/command_tower/messaging/contract/results/communication_result.rb +1 -0
  12. data/app/services/command_tower/messaging/inbox/conversation_result.rb +31 -0
  13. data/app/services/command_tower/messaging/inbox/entry_result.rb +36 -0
  14. data/app/services/command_tower/messaging/inbox/reader.rb +231 -18
  15. data/app/services/command_tower/messaging/inbox.rb +4 -0
  16. data/app/services/command_tower/messaging/rendering/inbox_document_renderer.rb +53 -36
  17. data/app/services/command_tower/messaging/rendering/inbox_presentation_resolver.rb +52 -0
  18. data/app/services/command_tower/messaging/rendering/inbox_presentation_snapshot.rb +64 -0
  19. data/app/services/command_tower/messaging.rb +5 -1
  20. data/app/services/command_tower/services/messaging/communications/produce.rb +4 -0
  21. data/app/services/command_tower/services/messaging/communications/produce_many.rb +6 -0
  22. data/app/services/command_tower/services/messaging/inbox.rb +47 -1
  23. data/app/workflows/command_tower/workflows/messaging/communications/produce_recipient_workflow.rb +5 -1
  24. data/app/workflows/command_tower/workflows/messaging/inbox.rb +15 -1
  25. data/config/routes.rb +1 -0
  26. data/db/migrate/20261010000001_add_inbox_presentation_snapshot_to_messaging_communications.rb +7 -0
  27. data/db/migrate/20261010000002_add_conversation_identity_to_messaging_communications.rb +10 -0
  28. data/docs/authorization.md +1 -1
  29. data/docs/bootstrap/00-ownership.md +77 -0
  30. data/docs/bootstrap/01-docker-make-compose.md +267 -0
  31. data/docs/bootstrap/02-create-the-rails-app.md +75 -0
  32. data/docs/bootstrap/03-pin-the-gem.md +53 -0
  33. data/docs/bootstrap/04-secrets-and-env.md +59 -0
  34. data/docs/bootstrap/05-install-migrate-doctor.md +74 -0
  35. data/docs/bootstrap/06-mount-and-health.md +53 -0
  36. data/docs/bootstrap/07-execution-bases.md +38 -0
  37. data/docs/bootstrap/08-initializer.md +90 -0
  38. data/docs/bootstrap/09-rbac.md +83 -0
  39. data/docs/bootstrap/10-roles-and-gates.md +62 -0
  40. data/docs/bootstrap/11-auth-client-path.md +57 -0
  41. data/docs/bootstrap/12-smoke-check.md +81 -0
  42. data/docs/bootstrap/13-optional.md +50 -0
  43. data/docs/bootstrap/14-sanity-checks.md +44 -0
  44. data/docs/bootstrap/README.md +90 -0
  45. data/docs/cookie_authentication_guide.md +11 -0
  46. data/docs/extending.md +3 -3
  47. data/docs/host_integration_guide.md +3 -281
  48. data/docs/initializing.md +37 -28
  49. data/docs/messaging_integration_guide.md +1 -1
  50. data/docs/principal_capabilities.md +1 -1
  51. data/docs/upgrades/0.10.0.md +5 -5
  52. data/docs/upgrades/0.11.0.md +5 -5
  53. data/docs/upgrades/0.18.0.md +35 -0
  54. data/docs/upgrades/README.md +2 -1
  55. data/lib/command_tower/authorization/default.yml +1 -0
  56. data/lib/command_tower/install/baseline.rb +2 -0
  57. data/lib/command_tower/version.rb +1 -1
  58. metadata +25 -2
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
 
@@ -42,11 +42,11 @@ Full install narrative: [initializing.md](initializing.md).
42
42
 
43
43
  | Surface | Notes |
44
44
  |---------|--------|
45
- | `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 |
46
46
  | `SKIP_CONFIGURE=1` | Migrations only |
47
47
  | `SKIP_MOUNT=1` | Initializer without mount |
48
48
  | `FORCE=1` | Overwrite initializer |
49
- | `rails g command_tower:configure` | `--skip-routes`, `--force` |
49
+ | `command_tower:configure` (`make rails ARGS='generate command_tower:configure'`) | `--skip-routes`, `--force` |
50
50
 
51
51
  ## Mounting and configuration
52
52
 
@@ -1,285 +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
- ## Step 11 — Client version compatibility (optional)
226
-
227
- CommandTower can gate or warn on stale client app versions per platform, ahead of every request (before authn/authz). It is entirely opt-in: an untouched registry is a no-op, and the shipped CommandTower-owned catalog is empty by design.
228
-
229
- ```ruby
230
- CommandTower.configure do |c|
231
- c.registry.client_compatibility.mode = :observe # or :enforce; default observe
232
-
233
- c.registry.client_compatibility.platform :web do |platform|
234
- platform.minimum = "1.0.0" # required; MAJOR.MINOR.PATCH[.prerelease]
235
- platform.recommended = "1.2.0" # optional; non-blocking upgrade guidance
236
- # platform.update_url is forbidden on :web (web recovery is "reload", not a store)
237
- end
238
-
239
- c.registry.client_compatibility.platform :ios do |platform|
240
- platform.minimum = "2.0.0"
241
- platform.update_url = "https://apps.apple.com/app/id..."
242
- end
243
-
244
- # Optional: a capability-scoped floor stricter than the platform-global minimum,
245
- # bound to an existing RBAC entity (see Step 4).
246
- c.registry.client_compatibility.entity :some_host_entity do |entity|
247
- entity.minimum.ios = "2.1.0"
248
- end
249
- end
250
- ```
251
-
252
- - Canonical platforms are `web` | `ios` | `android`. Configure only what your app ships — `android` unconfigured never fails boot on its own.
253
- - Zero configured platforms while otherwise opted in (non-default `mode`, any `entity`/`client_contract`/`bind`) fails boot fast; a completely untouched registry does not.
254
- - `X-App-Version` / `X-Client-Platform` request headers drive evaluation — never User-Agent, never an allowlist.
255
- - Every controller runs the check via `before_action :evaluate_client_compatibility!` on `CommandTower::ApplicationController`. Exempt a controller with `skip_before_action :evaluate_client_compatibility!`.
256
- - Process-level ENV overlays may only **raise** an already-configured platform's minimum, never lower it or configure a platform the host didn't enable: `COMMAND_TOWER_CLIENT_COMPATIBILITY_MODE=observe|enforce`, `COMMAND_TOWER_CLIENT_COMPATIBILITY_MINIMUM_WEB|IOS|ANDROID=<version>`.
257
- - In `:observe` mode nothing is ever blocked — decisions are logged only. In `:enforce` mode, an incompatible or unparseable identity renders HTTP `426` with a `client_update_required` error envelope.
258
- - Recommended-update guidance (when a client is compatible but below `recommended`) is surfaced only in `meta.clientCompatibility` on login and session-show responses — never on every response.
259
-
260
- ## Done when
261
-
262
- - Doctor passes for secrets/migrations
263
- - Host `rbac_groups.yml` maps Me/Auth entities
264
- - Users who should use Me surfaces have the host `member` (or equivalent) role
265
- - Needed feature gates are enabled
266
- - `GET /me` returns **200** with a Bearer token for a member user
267
-
268
- ## Common failures
269
-
270
- | Symptom | Likely cause |
271
- |---------|----------------|
272
- | Doctor green, `GET /me` is **403** | Missing host RBAC YAML or user lacks `member` (Steps 4–5) |
273
- | `POST /auth/plain-text/login` is **404** | `login.plain_text.enable?` off (Step 6) |
274
- | Password-reset / availability **404** | Matching feature gate off |
275
- | Phone / Pushover **503** | Capability / adapters not ready (Step 9) |
276
- | Confused error JSON on host controllers | Provisional `authenticate_user!` vs engine envelope (Step 7) |
277
-
278
- ## Related
279
-
280
- - [Initializing](initializing.md)
281
- - [Extending](extending.md)
282
- - [API reference](api_reference.md)
283
- - [Authentication & authorization guide](authentication_authorization_guide.md)
284
- - [Messaging](messaging_integration_guide.md)
285
- - [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
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  CommandTower owns modern messaging **platform** surfaces. Hosts supply product content, channel policy, and adapter credentials.
4
4
 
5
- Complete install and host RBAC first: [Host integration](host_integration_guide.md) (Steps 1–8 before emitting or wiring phone/Pushover).
5
+ Complete install and host RBAC first: [Bootstrap a new host](bootstrap/README.md) (through [`12-smoke-check.md`](bootstrap/12-smoke-check.md) before emitting or wiring phone/Pushover).
6
6
 
7
7
  ## Current surfaces
8
8
 
@@ -86,4 +86,4 @@ Me/Auth route gates (`session`, `me`, …) and unimplemented Admin entities are
86
86
  - [API reference](api_reference.md#get-authprincipal-capabilities)
87
87
  - [Authorization](authorization.md)
88
88
  - [Admin Workspace](admin_workspace.md) — tool manifest only; not a UI permission probe
89
- - [Host integration](host_integration_guide.md)
89
+ - [Bootstrap a new host](bootstrap/README.md)
@@ -3,7 +3,7 @@
3
3
  **From:** `0.5.0` (and earlier SchemaHelper-era hosts on `main`)
4
4
  **To:** `0.10.0`
5
5
 
6
- This document summarizes **all changes from `main` through the `0.10.0` release** (committed work on `schematizing` plus the modern platform working tree). It is the durable upgrade / change SST for hosts. For a sequenced blank-host path, use [Host integration](../host_integration_guide.md).
6
+ This document summarizes **all changes from `main` through the `0.10.0` release** (committed work on `schematizing` plus the modern platform working tree). It is the durable upgrade / change SST for hosts. For a sequenced blank-host path, use [Bootstrap a new host](../bootstrap/README.md).
7
7
 
8
8
  ## Breaking / host-impact
9
9
 
@@ -31,15 +31,15 @@ This document summarizes **all changes from `main` through the `0.10.0` release*
31
31
  ## Documentation
32
32
 
33
33
  - Canonical HTTP catalog: [API reference](../api_reference.md)
34
- - Host start-here: [Host integration](../host_integration_guide.md)
34
+ - Host start-here: [Bootstrap a new host](../bootstrap/README.md)
35
35
  - Install SST: [Initializing](../initializing.md)
36
36
  - Auth/cookie/RBAC guides refreshed to modern routes
37
37
  - Messaging and pagination aligned to Inbox + Produce contracts
38
38
 
39
39
  ## Upgrade checklist (existing hosts)
40
40
 
41
- 1. Bump gem to `0.10.0` and run `bin/rails command_tower:install:migrations` (or `SKIP_CONFIGURE=1 bin/rails command_tower:install`) then `bin/rails db:migrate`.
42
- 2. Set JWT + signup/recovery session secrets; run `bin/rails command_tower:doctor`.
41
+ 1. Bump gem to `0.10.0` and run `make rails ARGS='command_tower:install:migrations'` (or `SKIP_CONFIGURE=1 make rails ARGS='command_tower:install'`) then `make migrate`.
42
+ 2. Set JWT + signup/recovery session secrets; run `make doctor`.
43
43
  3. Add host `rbac_groups.yml` with a product role (typically `member`) that **grants** CT-owned entity names; do not redefine engine `owner` or copy CT controller mappings. Operational Admin roles are host-owned.
44
44
  4. Ensure users who need Me surfaces have the host `member` role (or equivalent).
45
45
  5. Enable feature gates you rely on (`login.plain_text.enable`, password reset, email verify, availability).
@@ -47,7 +47,7 @@ This document summarizes **all changes from `main` through the `0.10.0` release*
47
47
  7. Smoke: Bearer login → `GET /me` returns **200** (not **403**).
48
48
  8. If emitting messages: catalog, channels, adapters; call Produce from host workflows.
49
49
 
50
- New hosts: follow [Host integration](../host_integration_guide.md) end-to-end.
50
+ New hosts: follow [Bootstrap a new host](../bootstrap/README.md) end-to-end.
51
51
 
52
52
  ## Related
53
53
 
@@ -3,7 +3,7 @@
3
3
  **From:** `0.10.0`
4
4
  **To:** `0.11.0`
5
5
 
6
- This document summarizes **host-visible changes from `0.10.0` through the `0.11.0` release**. It is the durable upgrade / change SST for hosts already on the Auth/Me/Messaging platform. For a sequenced blank-host path, use [Host integration](../host_integration_guide.md).
6
+ This document summarizes **host-visible changes from `0.10.0` through the `0.11.0` release**. It is the durable upgrade / change SST for hosts already on the Auth/Me/Messaging platform. For a sequenced blank-host path, use [Bootstrap a new host](../bootstrap/README.md).
7
7
 
8
8
  ## Breaking / host-impact
9
9
 
@@ -67,13 +67,13 @@ See dummy composition in `rails_app/config/rbac_groups.yml`.
67
67
  - [Audit](../audit.md)
68
68
  - [Admin Workspace](../admin_workspace.md)
69
69
  - [Principal capabilities](../principal_capabilities.md)
70
- - Host start-here: [Host integration](../host_integration_guide.md)
70
+ - Host start-here: [Bootstrap a new host](../bootstrap/README.md)
71
71
  - Canonical HTTP catalog: [API reference](../api_reference.md)
72
72
 
73
73
  ## Upgrade checklist (existing 0.10.0 hosts)
74
74
 
75
- 1. Bump gem to `0.11.0` and run `bin/rails command_tower:install:migrations` then `bin/rails db:migrate`.
76
- 2. Run `bin/rails command_tower:doctor`.
75
+ 1. Bump gem to `0.11.0` and run `make rails ARGS='command_tower:install:migrations'` then `make migrate`.
76
+ 2. Run `make doctor`.
77
77
  3. Grant `principal_capabilities` (and `me_audit_events` if Account Activity is in product) on host `member` / equivalent.
78
78
  4. Explicitly grant Admin entities to **host-owned** operator roles (`admin_workspace`, `admin_users`, `admin_users_update`, `admin_rbac_assignments`, `admin_audit_events`, `admin_impersonation`) — do not assume a platform `admin` role.
79
79
  5. Point Admin FE gating at `GET /auth/principal-capabilities`, not `GET /admin/workspace`.
@@ -81,7 +81,7 @@ See dummy composition in `rails_app/config/rbac_groups.yml`.
81
81
  7. Smoke: login → `GET /auth/principal-capabilities` **200**; operator with grants → `GET /admin/workspace` **200**; without grants → **403**.
82
82
  8. Smoke: start impersonation → Admin routes **418**; `DELETE /auth/impersonation-session` restores actor.
83
83
 
84
- New hosts: follow [Host integration](../host_integration_guide.md) end-to-end (includes 0.10.0 foundations plus this release).
84
+ New hosts: follow [Bootstrap a new host](../bootstrap/README.md) end-to-end (includes 0.10.0 foundations plus this release).
85
85
 
86
86
  ## Related
87
87
 
@@ -0,0 +1,35 @@
1
+ # Upgrade: CommandTower 0.18.0
2
+
3
+ **From:** `0.17.0`
4
+ **To:** `0.18.0`
5
+
6
+ Minor release: inbox conversations. A host can stamp a stable key and title when it produces a message. The inbox list groups those messages. A conversation can be read as a window of at most 5 rendered documents. Accept stores the first presentation node's data so a later read does not recompute it. Two new migrations.
7
+
8
+ ## Host-visible changes
9
+
10
+ | Change | Host impact |
11
+ |--------|-------------|
12
+ | `Produce` `conversation_key` / `conversation_title` | Optional pair. Both or neither. Stored on the Communication. Not part of the accept fingerprint or metadata. A replay keeps the first pair. A produce with no pair stays null and remains a standalone inbox row. |
13
+ | `GET /me/inbox` | One entry per standalone message or per `conversation_key` in the requested scope. Each entry has `kind` (`message` or `conversation`), `id`, `title`, `preview`, `messageCount`, `unreadCount`, `read`, and `inboxItemIds`. Title and preview come from the latest in-scope member. |
14
+ | `GET /me/inbox/conversation` | Members for `key` and `scope`, oldest first. Does not mark them viewed. `messageCount`, `unreadCount`, and `inboxItemIds` describe the whole scope. `messages` is the rendered slice. |
15
+ | Window params | Optional and mutually exclusive: `around=oldest_unread`, `around=<inbox item id>`, `before`, or `after`, plus `limit`. A window renders at most 5. The first `around` window includes the oldest unread member (or the latest when none are unread), at most 2 older members, then newer members until the total is 5. Omitting the window params returns every member and no `window` key. |
16
+ | Cursor errors | A cursor outside this recipient, key, and scope is `422`. An unknown key stays `404`. `limit` above 5, or combined cursors, is `422`. |
17
+ | `inbox_presentation_snapshot` | Nullable JSON on `messaging_communications`. Accept captures the first presentation node inside the existing transaction. A failed capture stores `{ "v": 1, "omitted": true }`. No presentation node leaves the column null. Inbox detail reads the stored payload. |
18
+ | RBAC | `conversation` is a new action on the existing Me inbox entity. |
19
+
20
+ ## Host actions
21
+
22
+ 1. Bump gem to `0.18.0` (or `>= 0.18.0`).
23
+ 2. Install and migrate:
24
+ - `bundle exec rails command_tower:install:migrations`
25
+ - `bundle exec rails db:migrate`
26
+ - New files: `20261010000001_add_inbox_presentation_snapshot_to_messaging_communications`, `20261010000002_add_conversation_identity_to_messaging_communications`.
27
+ 3. Pass `conversation_key` and `conversation_title` from Produce only when a message should join a conversation. Existing producers that omit the pair stay standalone. There is no backfill.
28
+ 4. If the host copied the Me inbox action list instead of granting the entity, add `conversation`. An entity grant already includes the new action.
29
+ 5. If the host parses `GET /me/inbox` items as one row per inbox item, read the grouped entry shape instead.
30
+
31
+ ## Not in this release
32
+
33
+ - No backfill of historical conversation keys or presentation snapshots. Null stays null.
34
+ - No Conversation table. Grouping is the key column plus the list query.
35
+ - CommandTower frontend conversation feed, windowed scrolling, and narrow-web header. Those ship with the frontend package, not this gem.
@@ -4,6 +4,7 @@ Host-facing upgrade / change summaries for CommandTower releases.
4
4
 
5
5
  | Version | Summary |
6
6
  |---------|---------|
7
+ | [0.18.0](0.18.0.md) | Inbox conversations: optional Produce identity, grouped list entries, `GET /me/inbox/conversation` window of at most 5, accept-time presentation snapshot |
7
8
  | [0.17.0](0.17.0.md) | Client-version-compatibility gating (`observe`/`enforce`); Rich Messaging Inbox `content` documents; host-overridable rendering templates; shared email theming |
8
9
  | [0.16.0](0.16.0.md) | Me experience-states (`GET`/`POST complete`); `host_key`; `user_experience_states` migration; `me_experience_states` RBAC |
9
10
  | [0.15.0](0.15.0.md) | Me `POST /me/push/test` → Produce (`push_delivery_test`); configurable Expo self-test rate limit composers; `me_push#test` |
@@ -15,4 +16,4 @@ Host-facing upgrade / change summaries for CommandTower releases.
15
16
  | [0.11.0](0.11.0.md) | Execution context, audit ledger, Admin Workspace, principal capabilities, impersonation, Admin Users |
16
17
  | [0.10.0](0.10.0.md) | Modern Auth/Me/Messaging platform; SchemaHelper removal; host RBAC required |
17
18
 
18
- New hosts should start with [Host integration](../host_integration_guide.md).
19
+ New hosts should start with [Bootstrap a new host](../bootstrap/README.md).
@@ -33,6 +33,7 @@ entities:
33
33
  only:
34
34
  - index
35
35
  - show
36
+ - conversation
36
37
  - open
37
38
  - archive
38
39
  - destroy
@@ -16,6 +16,8 @@ module CommandTower
16
16
  20260817000003_create_command_tower_impersonation_sessions.rb
17
17
  20260826140000_add_deleted_at_to_users.rb
18
18
  20260906180000_create_user_experience_states.rb
19
+ 20261010000001_add_inbox_presentation_snapshot_to_messaging_communications.rb
20
+ 20261010000002_add_conversation_identity_to_messaging_communications.rb
19
21
  ].freeze
20
22
 
21
23
  module_function
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CommandTower
4
- VERSION = "0.17.0"
4
+ VERSION = "0.18.0"
5
5
  end