/logon-action
Make something happen for every user the next time they log on — once per user ever (a migration: re-pin their home, seed a record, flip a preference) or on every logon (a repair that must keep catching new work). Covers the once-vs-every decision, the copy-paste declaration for both the data route and the code route, the identity rule (runs as the USER; RunAs*, never Observable.Using(ImpersonateAsSystem)), how to verify it actually ran for a given user, and how to keep it deployment-specific so a portal that lacks the target nodes is a clean no-op instead of a dangling pin.
- Instructions: You need something to happen **for each user, at logon** — most often because an existing user is missing something a new user gets. Do **not** write a database migration that loops partition schemas patching `mesh_nodes`. Declare a **logon action**. Full treatment: [Logon Actions](/Doc/Architecture/LogonActions). This page is the operational one. # 1. Decide the mode first | | `RunOnce` | `EveryLogon` | |---|---|---| | Runs | at most once per user, ever | every logon | | Ledger | `User.CompletedLogonActions[actionId]` | none | | Use for | a one-time change the user then OWNS | a repair that must keep catching new work | | Example | swap their pinned items | adopt icons for apps installed since | **The test:** *can new work arrive after the first run?* - **No** → `RunOnce`. Re-running would clobber whatever the user has curated since. That is the whole reason the ledger is durable rather than a process flag. - **Yes** → `EveryLogon`, and it **must** carry a cheap "nothing to do" check. One query, zero writes in the steady state. An every-logon action without that check is a per-logon storm. # 2. Declare it — pick the route by whether every portal should have it ## Data route (deployment-specific — the default for anything naming content) Create a node at `Admin/_LogonAction/{id}`. No code, no image roll. **The node id is the ledger key.** ``` create @Admin/_LogonAction/docs-to-courses { "nodeType": "LogonAction", "name": "Swap the documentation pins for the courses", "state": "Active", "content": { "$type": "LogonAction", "description": "Existing users pinned the four doc sections; pin the courses instead.", "mode": "RunOnce", "order": 0, "enabled": true, "unpinPaths": ["Doc/Architecture", "Doc/DataMesh", "Doc/GUI", "Doc/AI"], "pinPaths": ["AgenticPrimer", "AgenticEngineering", "AgenticBusiness"] } } ``` `enabled: false` parks it without deleting it and **keeps the ledger**, so re-enabling does not re-run it for us…
- Auto mount: yes
- Launches sub thread: no