Appearance
TMS v2 phase 1 (platform): as-built spec
This folder describes the phase-1 platform code as it stands in the repos: schema, API, conditions, flows, jobs, settings, concurrency and gaps. It goes deep enough to extend or rebuild each part, and it exists so phases 2–7 keep the same pattern. For a one-meeting overview of the whole system, read the v2 reference instead.
Start with 00-patterns. It covers what every module shares. Then open the part you are working on.
Files
| file | part | read it for |
|---|---|---|
| 00-patterns.md | shared patterns | repo layout, module skeleton, registries, base service/schema, controller and error conventions, naming, web conventions, checklist for a new module |
| 02-auth.md | auth | login and session, /auth/me, roles, the permission registry (55 keys), guard, store scope (X-Store-Id), users admin, Roles/Users web pages |
| 03-logs-and-settings.md | logs, activity logs, settings | what gets logged and how long it is kept, record history, the settings module (6 areas, 31 keys), the foundation simplify cuts |
| 04-jobs-engine.md | jobs engine | fast and long lanes, leases, retry, DEAD rows, schedules, admin API, Kafka as a job target |
| 05-kafka.md | Kafka | outbound DO events, inbound consume (dedup, claim, DLQ), every topic |
| 06-media.md | media and attachments | S3 storage setting, presigned upload, attachment lifecycle, sweep, hidden-field guards |
| 07-template-export.md | templates and export | print/email/SMS render, script runner sandbox, field catalog (6 entities), export config |
Each part file uses the same 13 sections: summary · file map · data model · API · conditions · states and flows · jobs/events · settings and env · concurrency and recovery · web · tests · gaps · how to add the next one.
Which code this describes
| part | core-v2 | store-v2 | merged? |
|---|---|---|---|
| 03 to 07 | origin/main-v2-prototype 3d70475a | origin/main-v2-prototype 0851a3a | yes |
| 02 auth | feature/auth 9d9422c (base 3d70475) | feature/auth d9fd082 (base 0851a3a) | no, still in its own workspace (tms-v2-auth) |
Because auth is not merged yet, files 03–07 show the routes guarded by @Roles([admin]). The setting.* permission keys in 02-auth exist only on feature/auth. All facts come from reading the code. Nothing was run, so line numbers and behaviour under load are not runtime-checked.
Problems found while writing (code, not doc)
Each file's "Gaps" section has the full list. These are the ones that break something today:
| # | where | problem | file |
|---|---|---|---|
| 0 | every search endpoint | The client-built Mongo filter (convertFilter) goes straight into find(). Any operator or field gets through, $where and hidden fields included, and the keyword is used as an unescaped regex. Only the script runner blocks operators. | 00 |
| 1 | store-v2 Setting pages | Storage, Script settings and Outbound events pages call core routes that were removed in 3dd1c5a (/storage-settings, /script_settings, /outbound_events). They now get 404. | 03, 05, 06, 07 |
| 2 | storage default | Bucket is empty and allowed_extensions is [] by default, so every upload fails (503 or EXTENSION_NOT_ALLOWED). Because of #1 it cannot be fixed from the UI. | 03, 06 |
| 3 | auth web keys | web:src/constants/permission-keys.ts is hand-written. 9 of its 24 keys don't exist in core, so even a full admin loses 8 Setting menu items. | 02 |
| 4 | auth user states | Web expects active/locked/disabled, core sends active/locked_out/inactive/staged. Unlock never shows and Enable/Disable shows the wrong label. | 02 |
| 5 | Kafka DO consumer | Every error, including a temporary Mongo error, is treated as permanent, and the message goes to the DLQ. | 05 |
| 6 | Kafka off switch | The enabled flag (global and per event) is never checked on send, so turning Kafka off doesn't stop DO-update messages. | 04, 05 |
| 7 | Kafka controller | /v1/kafka/status and /test-connection have no role guard. Any logged-in user, a driver included, can make a pod connect to any broker. | 03, 05 |
| 8 | export | Export run is not built: configs can be saved, but nothing produces a file. | 07 |
Pattern drift, to settle before phase 2 so new modules have one rule to follow (detail in 00-patterns §11):
- List responses come in 5 different shapes.
- Concurrency control comes in 3 kinds:
rev,revision, and none (last write wins). not_foundis returned as 400 in some places and 404 in others.- A duplicate key returns 500, 400 or 409 depending on the module.
- Route names mix singular and plural.
Smaller items found:
- Nothing is ever deleted: DEAD jobs, DLQ rows and orphaned S3 objects stay forever.
- No schedule is declared anywhere.
- The first settings save freezes the code defaults into the record.
- Liquid output in templates is not HTML-escaped.