@smartkomm/plexus-cli 0.0.0-stage → 0.1.1
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.
- package/README.md +863 -2
- package/dist/cli.mjs +5432 -0
- package/dist/skill/.managed-by-plexus-cli +1 -0
- package/dist/skill/SKILL.md +224 -0
- package/dist/skill/references/accounts/email-changes.md +46 -0
- package/dist/skill/references/agents/use-shared-tools.md +52 -0
- package/dist/skill/references/applications/inspect-process-store.md +61 -0
- package/dist/skill/references/applications/start-a-test-application.md +66 -0
- package/dist/skill/references/audit/answer-access-request.md +115 -0
- package/dist/skill/references/config/attach-a-template.md +117 -0
- package/dist/skill/references/config/init-tenant.md +121 -0
- package/dist/skill/references/config/onboard-tenant.md +71 -0
- package/dist/skill/references/config/rename-program-stage.md +99 -0
- package/dist/skill/references/config/set-tenant-branding.md +150 -0
- package/dist/skill/references/config/set-tenant-terms.md +50 -0
- package/dist/skill/references/config/set-up-billing-catalog.md +73 -0
- package/dist/skill/references/config/update-tenant-config.md +174 -0
- package/dist/skill/references/config/wipe-tenant-config.md +107 -0
- package/dist/skill/references/export/create-named-export.md +47 -0
- package/dist/skill/references/feedback/create-and-triage-feedback.md +69 -0
- package/dist/skill/references/feedback/manage-feedback-tags.md +57 -0
- package/dist/skill/references/finance/change-contract-grant.md +36 -0
- package/dist/skill/references/finance/record-submission-date.md +33 -0
- package/dist/skill/references/interessenten/list-interessenten.md +54 -0
- package/dist/skill/references/marketing/demo-video-links.md +101 -0
- package/dist/skill/references/notifications/debug-mail-delivery.md +77 -0
- package/dist/skill/references/notifications/mail-level.md +90 -0
- package/dist/skill/references/notifications/reword-a-letter.md +155 -0
- package/dist/skill/references/notifications/trace-a-login.md +50 -0
- package/dist/skill/references/offer/add-workflow-node.md +268 -0
- package/dist/skill/references/offer/addresses.md +129 -0
- package/dist/skill/references/offer/appointment-task.md +79 -0
- package/dist/skill/references/offer/assignment-rule.md +195 -0
- package/dist/skill/references/offer/attach-form-to-workflow.md +79 -0
- package/dist/skill/references/offer/author-form-scenarios.md +97 -0
- package/dist/skill/references/offer/author-manifest-meta.md +94 -0
- package/dist/skill/references/offer/author-smartform.md +102 -0
- package/dist/skill/references/offer/bind-node-input.md +76 -0
- package/dist/skill/references/offer/completeness-check.md +137 -0
- package/dist/skill/references/offer/countersign-node.md +91 -0
- package/dist/skill/references/offer/create-pedagogy-unit.md +55 -0
- package/dist/skill/references/offer/declare-node-reads.md +99 -0
- package/dist/skill/references/offer/edit-live-tenant-offer.md +144 -0
- package/dist/skill/references/offer/edit-smartform.md +121 -0
- package/dist/skill/references/offer/edit-workflow-node.md +126 -0
- package/dist/skill/references/offer/engage-push-node.md +147 -0
- package/dist/skill/references/offer/insert-and-rewire-node.md +67 -0
- package/dist/skill/references/offer/node-tasks.md +162 -0
- package/dist/skill/references/offer/optional-assessment.md +65 -0
- package/dist/skill/references/offer/publish-offer-bundle.md +117 -0
- package/dist/skill/references/offer/set-offer-capacities.md +78 -0
- package/dist/skill/references/offer/staff-input-if-node.md +112 -0
- package/dist/skill/references/offer/task-appointment-result.md +69 -0
- package/dist/skill/references/offer/task-family-request.md +69 -0
- package/dist/skill/references/offer/task-result-files.md +52 -0
- package/dist/skill/references/offer/template-document-node.md +113 -0
- package/dist/skill/references/offer/workflow-default-channels.md +69 -0
- package/dist/skill/references/resolver/manage-contract-resolver.md +161 -0
- package/dist/skill/references/retention/erase-due-applications.md +99 -0
- package/dist/skill/references/retention/erase-due-parties.md +107 -0
- package/dist/skill/references/roles/migrate-parent-assignments.md +31 -0
- package/dist/skill/references/signing/README.md +113 -0
- package/dist/skill/references/signing/connect-a-school.md +90 -0
- package/dist/skill/references/signing/rotate-the-key.md +41 -0
- package/dist/skill/references/stammdaten/purge-deleted-person.md +68 -0
- package/dist/skill/references/stripe/connect-a-school.md +107 -0
- package/dist/skill/references/stripe/request-and-verify-a-payment.md +78 -0
- package/dist/skill/references/test-actions/drive-a-test-application.md +69 -0
- package/dist/skill/references/users/add-platform-admin.md +59 -0
- package/dist/skill/references/users/add-platform-user.md +59 -0
- package/dist/skill/references/users/add-tenant-admin.md +75 -0
- package/dist/skill/references/users/change-login-email.md +55 -0
- package/dist/skill/references/users/cli-access-for-tenant-admin.md +117 -0
- package/dist/skill/references/users/delete-permission-group.md +76 -0
- package/dist/skill/references/users/grant-cross-tenant-access.md +57 -0
- package/dist/skill/references/users/grant-feedback-assignable.md +43 -0
- package/dist/skill/references/users/set-password.md +57 -0
- package/dist/skill/references/users/use-test-account.md +79 -0
- package/dist/skill/references/worker-ops/debug-worker-jobs.md +90 -0
- package/dist/skill/references/worker-ops/verify-migrations.md +51 -0
- package/dist/spec.snapshot.json +156821 -0
- package/package.json +53 -4
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Change a user's login e-mail
|
|
2
|
+
|
|
3
|
+
**When to use:** an account's login address has to move — above all a made-up
|
|
4
|
+
test account on a real, deliverable domain (`test100@<domain>.com`) that must go to a
|
|
5
|
+
reserved one (`test100@<domain>.test`) so no mail can ever reach a stranger.
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
- A platform token with `_platform.users.manage` for the target env.
|
|
9
|
+
- The account exists (`plexus-cli --env <env> users show <email>`).
|
|
10
|
+
- The new address is free: the verb refuses one held by another account, in the
|
|
11
|
+
plan already.
|
|
12
|
+
|
|
13
|
+
## Steps
|
|
14
|
+
```bash
|
|
15
|
+
# 1. Plan — runs every check, writes nothing
|
|
16
|
+
plexus-cli --env <env> users set-email <old@domain> --to <new@domain>
|
|
17
|
+
|
|
18
|
+
# 2. Apply
|
|
19
|
+
plexus-cli --env <env> users set-email <old@domain> --to <new@domain> --confirm
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
For a test account, keep the local part and replace the domain's TLD with
|
|
23
|
+
`.test` (RFC 2606/6761): `test100@<domain>.com` → `test100@<domain>.test`,
|
|
24
|
+
`ada@<schule>.de` → `ada@<schule>.test`.
|
|
25
|
+
|
|
26
|
+
## Verify
|
|
27
|
+
```bash
|
|
28
|
+
plexus-cli --env <env> users show <new@domain> # same userId, same tenants
|
|
29
|
+
plexus-cli --env <env> users show <old@domain> # → No user with email …
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Idempotency / gotchas
|
|
33
|
+
- Re-running with the address already moved prints "no change".
|
|
34
|
+
- One identity, every tenant: an account shared by several tenants moves in all
|
|
35
|
+
of them at once. (Staff editing the party's E-Mail field cannot do this — that
|
|
36
|
+
path refuses a shared identity and points here.)
|
|
37
|
+
- The password is untouched; set one afterwards with `users set-password` if needed.
|
|
38
|
+
- **Mailbox proof** is reset (the holder re-confirms via a mailed link before
|
|
39
|
+
they can move a process again) — except for a reserved test address (`.test`,
|
|
40
|
+
`.example`, `.invalid`, `.localhost`, `example.com/.net/.org`): no mailbox
|
|
41
|
+
exists there, so the proof stays as it was (`proof: unchanged`), and no mode ever transmits
|
|
42
|
+
such an address (`skipped: reserved_address`).
|
|
43
|
+
- **Config-declared accounts** (a staff roster in tenant config): re-export the
|
|
44
|
+
tenant config afterwards (`plexus-cli --env <env> --tenant <slug> config export`).
|
|
45
|
+
Applying an older export that still names the old address re-creates an
|
|
46
|
+
identity on it — config accounts are matched by e-mail.
|
|
47
|
+
- Only the login changes. Old addresses in history (sent deliveries, value
|
|
48
|
+
history, contract snapshots) stay as they were; a queued delivery to the old
|
|
49
|
+
address ends `skipped: recipient_changed`.
|
|
50
|
+
- An account-LESS party's e-mail (a contact, a second parent) is a stored value,
|
|
51
|
+
not a login — change it with `plexus-cli call parties:updateValue …` instead.
|
|
52
|
+
|
|
53
|
+
## Related
|
|
54
|
+
- [Set (rotate) a user's password](./set-password.md)
|
|
55
|
+
- [Debug a mail delivery](../notifications/debug-mail-delivery.md)
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# CLI access for a tenant admin
|
|
2
|
+
|
|
3
|
+
**When to use:** a school's own administrator (no platform rights) should run
|
|
4
|
+
`plexus-cli` for **exactly their tenant**: export, plan and apply its config
|
|
5
|
+
(`config export|apply|set`) and its admission offers (`offer …`). Example: the
|
|
6
|
+
administrator of one school group onboarding their own offers.
|
|
7
|
+
|
|
8
|
+
The person gets a `plx_…` token that acts **as them**. It reaches exactly the
|
|
9
|
+
tenants their own login reaches — no other tenant, no platform surface. **A token
|
|
10
|
+
works only while its owner holds `administration.apiTokens.manage`** (platform:
|
|
11
|
+
`_platform.apiTokens.manage`): every CLI call asks the right again, and the
|
|
12
|
+
change that takes the right away revokes the person's tokens.
|
|
13
|
+
|
|
14
|
+
## Prerequisites
|
|
15
|
+
- **The person is a tenant member with a login.** No login yet →
|
|
16
|
+
[Add a tenant admin](./add-tenant-admin.md) first.
|
|
17
|
+
- **Every `tenant_admin` holds the three CLI rights** — `administration.apiTokens.manage`
|
|
18
|
+
(mint a token), `administration.config.read` / `.manage` (config, held
|
|
19
|
+
**tenant-wide**). They are part of the derived `tenant_admin` and reach existing
|
|
20
|
+
tenants on every deploy; nothing to grant. Someone outside `tenant_admin` needs a
|
|
21
|
+
group holding them.
|
|
22
|
+
- **Offer commands** need `bewerbungsmanagement.forms.configure` (in `tenant_admin`).
|
|
23
|
+
A config change to groups or members also needs
|
|
24
|
+
`administration.permissions.manage` / `.users.manage` (in `tenant_admin`).
|
|
25
|
+
- **Your auth (issuing for them):** a platform token with `_platform.users.manage`
|
|
26
|
+
**and** `_platform.apiTokens.manage` — the token acts as the person.
|
|
27
|
+
- `--env` is required for the write; prod also needs `--allow-prod`.
|
|
28
|
+
|
|
29
|
+
## Steps
|
|
30
|
+
|
|
31
|
+
**0 — check the person is a `tenant_admin`:**
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
plexus-cli --env <env> users show <email> # groups: tenant_admin, …
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**A — the administrator mints it themselves:** in the app, *Einstellungen →
|
|
38
|
+
API-Token* („Token erstellen"), or with a token they already have:
|
|
39
|
+
`plexus-cli --env <env> tokens create laptop`.
|
|
40
|
+
|
|
41
|
+
**B — the platform issues it for them** (to hand over directly):
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
plexus-cli --env <env> tokens create cli --for <email> --output /tmp/token-<slug>.csv --quiet
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
- The token is shown **once**. `--output` appends `email,token` to the CSV (a file
|
|
48
|
+
with a different header, e.g. a password file, is refused); `--quiet` keeps it out
|
|
49
|
+
of stdout. Without `--output`, it is printed.
|
|
50
|
+
- Refused when the address has no account (`kein Konto`), no login, or holds
|
|
51
|
+
`administration.apiTokens.manage` in no tenant (`in keinem Mandanten`) — including
|
|
52
|
+
platform operators, who are tenant-less.
|
|
53
|
+
- The token records who issued it.
|
|
54
|
+
|
|
55
|
+
**The administrator's setup** (`~/.config/agent-tools/plexus.yml`; see SKILL.md →
|
|
56
|
+
Connect, or `plexus-cli --help`). The platform operator provides the staging URLs:
|
|
57
|
+
|
|
58
|
+
```yaml
|
|
59
|
+
plexus:
|
|
60
|
+
default_env: staging
|
|
61
|
+
default_tenant: <slug>
|
|
62
|
+
environments:
|
|
63
|
+
staging: { convex_url: "https://<staging-convex-url>", site_url: "https://<staging-site-url>", token: "plx_…" }
|
|
64
|
+
# Required even without a prod token: every write compares its target with
|
|
65
|
+
# the prod URL (refused otherwise: "names no environments.prod.convex_url").
|
|
66
|
+
prod: { convex_url: "https://db.plexus.swop.schule", site_url: "https://actions.db.plexus.swop.schule" }
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Verify
|
|
70
|
+
|
|
71
|
+
As the administrator's token:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
plexus-cli --env <env> whoami
|
|
75
|
+
plexus-cli --env <env> config export --tenant <slug> --out ./<slug>/config.json # passes
|
|
76
|
+
plexus-cli --env <env> config export --tenant <other-slug> --out /tmp/x.json # FORBIDDEN / NOT_FOUND
|
|
77
|
+
plexus-cli --env <env> config apply --tenant <slug> --from snapshot:./<slug>/config.json # dry run passes
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## What a tenant-scoped apply refuses (FORBIDDEN, naming the field)
|
|
81
|
+
- a tenant that does not exist (no onboarding), `--from blueprint`, `--allow-destructive`;
|
|
82
|
+
- any `tenant.*` setting except `branding`, `brandingAssets`, `notificationChrome`
|
|
83
|
+
differing from live — modules (`enabledModules` decides the permission allowlist),
|
|
84
|
+
`defaultApplicantGroupKey`, `mailLevel`, `magicLinkLogin`, terms, …;
|
|
85
|
+
- the import `source`;
|
|
86
|
+
- a `foundation.users` address that is not an **active** member with a login (no new
|
|
87
|
+
accounts, no other tenant's people, no revoked member brought back);
|
|
88
|
+
- a `_platform.*` right in any permission group.
|
|
89
|
+
|
|
90
|
+
A change to the permission groups additionally needs `administration.permissions.manage`,
|
|
91
|
+
a change to `foundation.users` (who sits in which group) `administration.users.manage` —
|
|
92
|
+
the rights the app asks for the same act (`tenant_admin` holds both).
|
|
93
|
+
|
|
94
|
+
Copy those values from `config export` unchanged; a change to them is a platform task.
|
|
95
|
+
Platform-only regardless: `config wipe`, `config prune-offers`, `config apply --scope
|
|
96
|
+
platform`, setup mode (Load N / wipe), `offer hard-delete`.
|
|
97
|
+
|
|
98
|
+
## Idempotency / gotchas
|
|
99
|
+
- Minting again makes a **second** token; revoke the old one with `tokens list` →
|
|
100
|
+
`tokens revoke <id>`. Without the right, the CLI refuses every call — listing and
|
|
101
|
+
revoking your own tokens then works in the app (Settings → API-Token) only.
|
|
102
|
+
- Taking the right away (group removed or narrowed, tenant access ended, user
|
|
103
|
+
deactivated, module dropped by `config apply` or the deploy, platform right
|
|
104
|
+
dropped) **revokes** the person's tokens in the same change. Giving the
|
|
105
|
+
right back does not revive them — mint a new one. The exchange then answers 401
|
|
106
|
+
„Das API-Token „…“ wurde am <Datum> widerrufen: <Grund>“. A right that lapses without a
|
|
107
|
+
write is still caught: every CLI call asks it and refuses with 403 „Ihr Konto hat
|
|
108
|
+
das Recht „API-Tokens: Verwalten“ nicht mehr …“.
|
|
109
|
+
- A tenant member cannot be made platform admin (`users set-platform-admin`):
|
|
110
|
+
platform rights and tenant access exclude each other — an inactive access row
|
|
111
|
+
counts too.
|
|
112
|
+
- A unit-scoped membership does not count for config: the right must be held
|
|
113
|
+
tenant-wide.
|
|
114
|
+
|
|
115
|
+
## Related
|
|
116
|
+
- [Add a tenant admin](./add-tenant-admin.md) — the login itself.
|
|
117
|
+
- [Edit a live tenant's offer](../offer/edit-live-tenant-offer.md).
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Delete a tenant permission group
|
|
2
|
+
|
|
3
|
+
Remove a permission group that should not exist — typically one created by an
|
|
4
|
+
accidental click in Administration → Berechtigungen, which the admin UI can
|
|
5
|
+
create and rename but **not** delete.
|
|
6
|
+
|
|
7
|
+
## When to use
|
|
8
|
+
|
|
9
|
+
- A stray group ("Neue Berechtigungsgruppe") with no members.
|
|
10
|
+
- A team that was retired and whose work has been re-pointed elsewhere.
|
|
11
|
+
|
|
12
|
+
Not for: taking someone's access away — that is `users unset-group` (leave the
|
|
13
|
+
group, keep the tenant) or `users remove` (revoke tenant access).
|
|
14
|
+
|
|
15
|
+
## Prerequisites
|
|
16
|
+
|
|
17
|
+
- `bewerbungsmanagement`-independent: the mutation gates on the tenant's
|
|
18
|
+
**permissions-manage** grant, so a tenant admin or platform admin token.
|
|
19
|
+
- The group must be genuinely unreferenced (see *Guards*).
|
|
20
|
+
|
|
21
|
+
## Commands
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# 1. See what the tenant has.
|
|
25
|
+
plexus-cli --env prod users groups --tenant <slug>
|
|
26
|
+
|
|
27
|
+
# 2. Dry-run — prints the member count it would be deleting against.
|
|
28
|
+
plexus-cli --env prod users groups delete neue_berechtigungsgruppe \
|
|
29
|
+
--tenant <slug>
|
|
30
|
+
|
|
31
|
+
# 3. Commit.
|
|
32
|
+
plexus-cli --env prod users groups delete neue_berechtigungsgruppe \
|
|
33
|
+
--tenant <slug> --confirm --allow-prod
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Verify
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
plexus-cli --env prod users groups --tenant <slug> # key is gone
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Config is DB-authoritative, so the group is gone from the tenant's config too. A
|
|
43
|
+
platform operator can confirm it in a `config export` (`foundation.permissionGroups`).
|
|
44
|
+
|
|
45
|
+
## Guards (the delete REFUSES when…)
|
|
46
|
+
|
|
47
|
+
| Refusal | Meaning | Fix first |
|
|
48
|
+
|---|---|---|
|
|
49
|
+
| `has-members` | Someone is still in the group (the message names them) | `users unset-group` each |
|
|
50
|
+
| `owns-teams` | A running application binds this group as a work owner | Re-assign those applications |
|
|
51
|
+
| `referenced-by-workflow` | A stored graph routes a decision/assign (or a not-yet-migrated legacy team-owner binding) to this key | Re-point the node, re-publish |
|
|
52
|
+
| `structural` | It is `tenant_admin` | — (never deletable) |
|
|
53
|
+
| `default-applicant` | It is the tenant's `defaultApplicantGroupKey` | Point the default at another group |
|
|
54
|
+
| `unknown` | No group with that key in this tenant | Check `users groups` |
|
|
55
|
+
|
|
56
|
+
## Gotchas
|
|
57
|
+
|
|
58
|
+
- **Hard delete, on purpose.** There is no soft delete and no `--restore`. A
|
|
59
|
+
hidden-but-present group would need an `active` flag every reader honours, and
|
|
60
|
+
a reader that forgets one leaves a ghost — exactly the bug that let a removed
|
|
61
|
+
user keep a `user_tenant_access` row and made `config export` throw for that
|
|
62
|
+
tenant. Re-creating an empty group is one click; a ghost is a day of debugging.
|
|
63
|
+
- **The group's `custom_field_permissions` go with it.** Those grants are the
|
|
64
|
+
group's own field visibility settings and mean nothing without it. Everything
|
|
65
|
+
else refuses rather than cascades.
|
|
66
|
+
- **Key, not label.** `users groups delete` takes the stable `key`
|
|
67
|
+
(`neue_berechtigungsgruppe`), never the display label — renaming a group must
|
|
68
|
+
not change what the command targets.
|
|
69
|
+
|
|
70
|
+
## Related
|
|
71
|
+
|
|
72
|
+
- [Add a tenant admin](./add-tenant-admin.md)
|
|
73
|
+
- [Update a tenant's config](../config/update-tenant-config.md) — the declarative
|
|
74
|
+
route: drop the group from the exported config and `config apply --confirm
|
|
75
|
+
--allow-destructive` sweeps it as an orphan. Equivalent outcome; use the CLI
|
|
76
|
+
verb for a one-off, the config route when the change belongs in the file.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Grant every tenant's users access to another tenant
|
|
2
|
+
|
|
3
|
+
**When to use:** let **all existing users of every tenant** reach one shared tenant
|
|
4
|
+
— the canonical case is giving every client access to the **Test Tenant** (`test`)
|
|
5
|
+
so they can dogfood the product. One-time + manual: it grants the users that exist
|
|
6
|
+
**now**; new users are **not** auto-enrolled (re-run later to pick up newcomers).
|
|
7
|
+
|
|
8
|
+
> Pair this with the Test-Tenant feedback-privacy rule: the Test Tenant grants no
|
|
9
|
+
> `administration.feedback.viewAll` (part of the `test` tenant's config), so
|
|
10
|
+
> "Alle Rückmeldungen" is unavailable
|
|
11
|
+
> there and each client only sees their own feedback.
|
|
12
|
+
|
|
13
|
+
## Prerequisites
|
|
14
|
+
- **Env:** target with `--env <local|staging|prod>` (this is a platform operation —
|
|
15
|
+
no `--tenant` needed). `plexus-cli --env <env> tenants` lists the reachable slugs.
|
|
16
|
+
- **Auth:** a platform-admin token with `_platform.users.manage`. Confirm with
|
|
17
|
+
`plexus-cli --env <env> whoami`.
|
|
18
|
+
- **Tooling:** an installed `plexus-cli` (`plexus-cli users grant-all --help`).
|
|
19
|
+
|
|
20
|
+
## Steps
|
|
21
|
+
Run once per **source** tenant, pointing every one at the target (`test`):
|
|
22
|
+
```bash
|
|
23
|
+
# Repeat for each non-test tenant slug:
|
|
24
|
+
plexus-cli --env <env> users grant-all --from <sourceSlug> --to test --group tenant_admin
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
To enumerate the source slugs to loop over:
|
|
28
|
+
```bash
|
|
29
|
+
plexus-cli --env <env> tenants # list slugs; run grant-all for each but `test`
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
- `--group tenant_admin` (the default) lets the granted users explore every module in
|
|
33
|
+
the Test Tenant. Pass a different group key for a narrower role.
|
|
34
|
+
- Each user is granted idempotently; a user who already has access is a no-op.
|
|
35
|
+
- Platform operators (tenant-less identities) never appear in a tenant's user set and
|
|
36
|
+
are additionally rejected by the `_platform.* XOR tenant-access` invariant — they
|
|
37
|
+
show up under `skipped` with a reason rather than aborting the run.
|
|
38
|
+
|
|
39
|
+
## Verify
|
|
40
|
+
```bash
|
|
41
|
+
plexus-cli --env <env> users show <someEmailFromSource>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Success: the user now lists the `test` tenant with group `tenant_admin, applicant`.
|
|
45
|
+
The command output also prints `Granted N/M users …` plus any `skipped` rows.
|
|
46
|
+
|
|
47
|
+
## Idempotency / gotchas
|
|
48
|
+
- **Current users only** — there is no standing auto-grant; re-run to enroll users
|
|
49
|
+
added after the last run.
|
|
50
|
+
- Re-running is safe (idempotent per user); it neither rotates passwords nor creates
|
|
51
|
+
logins (contrast `users provision`).
|
|
52
|
+
- Runtime-only grants — a destructive reseed of the source/target tenant drops them;
|
|
53
|
+
re-run this playbook afterwards.
|
|
54
|
+
|
|
55
|
+
## Related
|
|
56
|
+
- [Add a tenant admin](./add-tenant-admin.md) — single ready-to-log-in account.
|
|
57
|
+
- Command reference: `plexus-cli users grant-all --help`.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Make a platform admin assignable to feedback issues
|
|
2
|
+
|
|
3
|
+
**When to use:** a platform admin should appear in the feedback-board assignee
|
|
4
|
+
picker (and be a valid `feedback assign` target). Being a platform admin is **not**
|
|
5
|
+
enough — assignability is the opt-in `_platform.feedback.assign` permission, granted
|
|
6
|
+
explicitly to the platform's real triage admins only.
|
|
7
|
+
|
|
8
|
+
## Prerequisites
|
|
9
|
+
- `--env staging|prod` configured with a platform-operator token (the grant is gated
|
|
10
|
+
`_platform.users.manage`).
|
|
11
|
+
- The target already exists as a platform admin (`plexus-cli --env <env> platform users`).
|
|
12
|
+
|
|
13
|
+
## Steps
|
|
14
|
+
```bash
|
|
15
|
+
plexus-cli --env <env> users set-platform-permission <email> \
|
|
16
|
+
--permission _platform.feedback.assign --on
|
|
17
|
+
```
|
|
18
|
+
Example — grant one triage admin on prod:
|
|
19
|
+
```bash
|
|
20
|
+
plexus-cli --env prod users set-platform-permission --allow-prod ada@example.com \
|
|
21
|
+
--permission _platform.feedback.assign --on
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Verify
|
|
25
|
+
```bash
|
|
26
|
+
# The assignee picker lists EXACTLY the assignable admins:
|
|
27
|
+
plexus-cli --env <env> feedback admins
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Idempotency / gotchas
|
|
31
|
+
- **Opt-in, not part of the full-admin grant.** Making someone a full platform admin
|
|
32
|
+
(`users set-platform-admin … --on`) does **not** grant assignability; this perm is
|
|
33
|
+
excluded from the default set on purpose. Re-affirming full-admin status *preserves*
|
|
34
|
+
an already-granted assign perm (it is never silently stripped).
|
|
35
|
+
- Revoke with `--off`. Removing the last permission from a row deletes the
|
|
36
|
+
`platform_admins` row entirely (the identity stays a normal user).
|
|
37
|
+
- `_platform.* XOR tenant-access`: the grant fails loud if the identity has tenant
|
|
38
|
+
access (platform operators must be tenant-less).
|
|
39
|
+
- Re-running `--on` is a no-op (the perm is added once).
|
|
40
|
+
|
|
41
|
+
## Related
|
|
42
|
+
- [Add a platform admin](./add-platform-admin.md) · [Add a platform user](./add-platform-user.md)
|
|
43
|
+
- [Create + triage feedback from the CLI](../feedback/create-and-triage-feedback.md)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Set (rotate) a user's password
|
|
2
|
+
|
|
3
|
+
**When to use:** an existing user — tenant user **or** platform admin — needs a new
|
|
4
|
+
login password (reset a lost one, or set a known value). This only changes the
|
|
5
|
+
password; it grants nothing and creates nothing.
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
- **Env:** target with `--env <local|staging|prod>`. No `--tenant` — the command
|
|
9
|
+
finds the identity by email globally.
|
|
10
|
+
- **Auth:** a token with `_platform.users.manage` (your platform-admin token, no
|
|
11
|
+
Convex admin key). Confirm with `plexus-cli --env <env> whoami`.
|
|
12
|
+
- **Inputs:** the user's email. The identity **must already exist** (provision it
|
|
13
|
+
first with [add-tenant-admin](./add-tenant-admin.md) /
|
|
14
|
+
[add-platform-admin](./add-platform-admin.md)).
|
|
15
|
+
- **Tooling:** an installed `plexus-cli` (`plexus-cli users set-password --help`).
|
|
16
|
+
|
|
17
|
+
## Steps
|
|
18
|
+
```bash
|
|
19
|
+
# Set a known password:
|
|
20
|
+
plexus-cli --env <env> users set-password <email> --password 'NewPassw0rd!'
|
|
21
|
+
|
|
22
|
+
# …or let the server generate a strong one (returned once; capture without echoing):
|
|
23
|
+
plexus-cli --env <env> users set-password <email> --output creds.csv --quiet
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The server owns hashing + generation and returns the plaintext **once** (only the
|
|
27
|
+
hash is stored). `--output creds.csv` appends the `email,password` row; `--quiet`
|
|
28
|
+
keeps it off stdout.
|
|
29
|
+
|
|
30
|
+
## Verify
|
|
31
|
+
```bash
|
|
32
|
+
plexus-cli --env <env> users show <email> # identity unchanged (sanity)
|
|
33
|
+
```
|
|
34
|
+
Then confirm the new password actually logs in at the env's `/login`.
|
|
35
|
+
|
|
36
|
+
## Hand the credentials over
|
|
37
|
+
|
|
38
|
+
Same three-line format as every other credential handover — URL, email, password,
|
|
39
|
+
bare, one block per account. See
|
|
40
|
+
[Add a tenant admin](./add-tenant-admin.md) → "Hand the credentials over".
|
|
41
|
+
|
|
42
|
+
## Idempotency / gotchas
|
|
43
|
+
- **Pure credential write** — it creates no identity, tenant access, person, or
|
|
44
|
+
permission, and changes no groups. Use `users provision` /
|
|
45
|
+
`users provision-platform-admin` to *create* a ready-to-log-in user;
|
|
46
|
+
`set-password` only rotates an **existing** one.
|
|
47
|
+
- **Fails loud, never repairs by creating:** throws if no Plexus identity exists for
|
|
48
|
+
the email, if the email has no live Better Auth account, or if that account
|
|
49
|
+
doesn't match the identity (an orphan → run `users relink-auth` first, then retry).
|
|
50
|
+
- The password is shown **once**; re-run to mint a fresh one if you lose it.
|
|
51
|
+
- Email match is case-insensitive (the backend lowercases it).
|
|
52
|
+
|
|
53
|
+
## Related
|
|
54
|
+
- [Add a tenant admin](./add-tenant-admin.md) · [Add a platform admin](./add-platform-admin.md)
|
|
55
|
+
— both also accept `--password` at creation time.
|
|
56
|
+
- `users relink-auth <email>` — repair an orphaned identity before setting a password.
|
|
57
|
+
- Command reference: `plexus-cli users set-password --help`.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Use a tenant's test account
|
|
2
|
+
|
|
3
|
+
**When to use:** you want to walk a school's actual admission process as an
|
|
4
|
+
applicant — on local, staging or prod — without registering a new address first.
|
|
5
|
+
|
|
6
|
+
## Prerequisites
|
|
7
|
+
|
|
8
|
+
- The account exists already in every tenant with an applicant portal:
|
|
9
|
+
`config:bootstrap` provisions it on every deploy, `config apply` on every
|
|
10
|
+
tenant onboarding, and the local development seed. Nothing to create.
|
|
11
|
+
- Identity: **`test@example.com`** ("Test Bewerber", stable key `fixture_test`
|
|
12
|
+
on `user_tenant_access.configKey`), a member of the tenant's applicant group.
|
|
13
|
+
- Staging/prod: a platform admin (`_platform.users.manage`) to set its password
|
|
14
|
+
once per deployment.
|
|
15
|
+
|
|
16
|
+
## Steps
|
|
17
|
+
|
|
18
|
+
### 1. Check the account in the target tenant
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
plexus-cli --env <local|staging|prod> users show test@example.com
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Expect one access row per tenant with an applicant portal, each in the
|
|
25
|
+
`applicant` group.
|
|
26
|
+
|
|
27
|
+
### 2. Set a password (staging / prod, once per deployment)
|
|
28
|
+
|
|
29
|
+
A deploy provisions the account with a random password nobody holds. Set one:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
plexus-cli --env <staging|prod> --tenant <slug> users set-password test@example.com
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The identity is global, so this one password opens the account in **every**
|
|
36
|
+
tenant of that deployment. The plaintext is printed once — password manager,
|
|
37
|
+
not a ticket.
|
|
38
|
+
|
|
39
|
+
**Local** needs no password: the account carries the dev-login marker, so
|
|
40
|
+
`/login?as=test@example.com&redirect=/<slug>/e` signs in as it.
|
|
41
|
+
|
|
42
|
+
### 3. Apply
|
|
43
|
+
|
|
44
|
+
Open `https://<host>/<slug>/e`, start the offer, fill the form — the same
|
|
45
|
+
surfaces a parent uses.
|
|
46
|
+
|
|
47
|
+
## Verify
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# It is the test row of "Wer ist der Antragsteller?" (needs testActions.run).
|
|
51
|
+
plexus-cli --env <env> call admission:testApplicantParties client=<slug>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Gotchas
|
|
55
|
+
|
|
56
|
+
- **Every application of it is a test application.** Started by the
|
|
57
|
+
account itself through the portal (any Testmodus checkbox is irrelevant) or
|
|
58
|
+
by staff from the internal „+ Bewerbung" dialog (its amber first row, for
|
|
59
|
+
anyone with `bewerbungsmanagement.testActions.run`; refused without it), the
|
|
60
|
+
server derives test mode from the key. Discard it like any test application.
|
|
61
|
+
- **Mail.** Never sent, in any mode (staging `redirect` included):
|
|
62
|
+
`example.com` is a reserved test address, so the delivery is rendered and
|
|
63
|
+
recorded and settles `skipped` / `reserved_address` — sign in with the
|
|
64
|
+
password, not a magic link.
|
|
65
|
+
- **A deploy never rotates the password.** An existing credential is preserved,
|
|
66
|
+
so a password you distributed survives every `config:bootstrap`.
|
|
67
|
+
- **Identity is the key, not the name.** Renaming "Test Bewerber" moves the row;
|
|
68
|
+
it never creates a second account.
|
|
69
|
+
- **A real account on `test@example.com` fails the deploy** (`config:bootstrap`
|
|
70
|
+
throws "not the test fixture"). Move that account to another address first.
|
|
71
|
+
- **Not in `config export`.** The account is platform-provisioned, so it is
|
|
72
|
+
never emitted as a config user.
|
|
73
|
+
- Skipped (and named in the bootstrap result under `fixtures.skipped`): tenants
|
|
74
|
+
without a `defaultApplicantGroupKey`, and the reserved `_platform` tenant.
|
|
75
|
+
|
|
76
|
+
## Related
|
|
77
|
+
|
|
78
|
+
- [Drive a test application through test mode](../test-actions/drive-a-test-application.md)
|
|
79
|
+
- [Set (rotate) a user's password](./set-password.md)
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Debug a stuck / failed import or worker job
|
|
2
|
+
|
|
3
|
+
**When to use:** an import shows "running" but looks stuck, the Worker console
|
|
4
|
+
shows no active worker, a job failed and you need to know why, or you must clean
|
|
5
|
+
up a stranded import run. This is the **canonical agent debugging setup** for the
|
|
6
|
+
worker/job system: `plexus-cli` for the LIVE job/worker/import state +
|
|
7
|
+
`glitchtip-cli` for the captured stack traces.
|
|
8
|
+
|
|
9
|
+
## Prerequisites
|
|
10
|
+
- `--env local|staging|prod`; the new worker/job commands need **platform-admin**
|
|
11
|
+
(the configured token already is, on staging/prod).
|
|
12
|
+
- For the stack traces: access to the platform's GlitchTip (the error tracker the
|
|
13
|
+
worker reports to), e.g. through `glitchtip-cli` (`glitchtip-cli info` shows the
|
|
14
|
+
server + auth). That is platform-operator access; without it, ask the operator.
|
|
15
|
+
|
|
16
|
+
## Steps
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# 1. Live fleet: is any worker alive, and what is it running right now?
|
|
20
|
+
plexus-cli --env prod platform workers --json # lifecycle live|stale|dead, currentJob
|
|
21
|
+
|
|
22
|
+
# 2. The jobs themselves (generic queue). Filter to what's stuck/broken.
|
|
23
|
+
plexus-cli --env prod jobs list --status running # genuinely in flight
|
|
24
|
+
plexus-cli --env prod jobs list --status failed --kind import
|
|
25
|
+
plexus-cli --env prod jobs show <jobId> # lease, attempts, phase, error, run timeline
|
|
26
|
+
plexus-cli --env prod jobs logs <jobId> # captured worker stdout/stderr
|
|
27
|
+
|
|
28
|
+
# 3. Import-domain view (imports stuck "still running" live here, NOT in jobs).
|
|
29
|
+
plexus-cli --env prod imports status --tenant <slug>
|
|
30
|
+
|
|
31
|
+
# 4. Captured exceptions for the failure (stack trace + tags).
|
|
32
|
+
glitchtip-cli issues list
|
|
33
|
+
# GlitchTip event tags the worker attaches: errorSource=backend:worker,
|
|
34
|
+
# jobKind, jobId, importRunId, configKey. Filter by them via GlitchTip's API
|
|
35
|
+
# (the CLI `issues list` has no tag filter).
|
|
36
|
+
|
|
37
|
+
# 5. Is the reaper (the only authority that frees stuck leases) actually firing?
|
|
38
|
+
plexus-cli --env prod workers reaper-status --json # ranAt should be < ~1 min old
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### The decision rule (why did the worker die?)
|
|
42
|
+
|
|
43
|
+
A job reaper-failed / vanished, **and**:
|
|
44
|
+
- **GlitchTip HAS an event** → it was a JS throw. Read the stack in the issue.
|
|
45
|
+
- **GlitchTip has NO event** → uncatchable **OOM/SIGKILL**. The only trace is the
|
|
46
|
+
reaper-failed job; its error message names the last phase + idle time (e.g.
|
|
47
|
+
*"vanished during phase 'importing' after N min"*). Confirming it needs a
|
|
48
|
+
platform operator with host access: the worker container's **exit code 137**
|
|
49
|
+
(or the kernel's OOM-killer log).
|
|
50
|
+
|
|
51
|
+
## Recovery
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# Force a reaper pass now instead of waiting for the 1-min cron.
|
|
55
|
+
plexus-cli --env prod workers reap --allow-prod --json
|
|
56
|
+
|
|
57
|
+
# Clear import runs stuck 'running' with no active job (the orphan class).
|
|
58
|
+
# DRY-RUN first (no --confirm), then confirm. Marks failed, never deletes.
|
|
59
|
+
plexus-cli --env prod imports cleanup-orphaned --tenant <slug>
|
|
60
|
+
plexus-cli --env prod imports cleanup-orphaned --allow-prod --tenant <slug> --confirm
|
|
61
|
+
plexus-cli --env prod imports cleanup-orphaned --all-tenants # dry-run sweep
|
|
62
|
+
|
|
63
|
+
# Cancel a job that should not be running.
|
|
64
|
+
plexus-cli --env prod jobs cancel --allow-prod <jobId>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Verify
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
plexus-cli --env prod imports status --tenant <slug> # the stuck run now 'failed'
|
|
71
|
+
plexus-cli --env prod jobs list --status running # nothing unexpectedly in flight
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Idempotency / gotchas
|
|
75
|
+
- `cleanup-orphaned` and `jobs cancel` flip rows to **failed/canceled**, never
|
|
76
|
+
delete — history is preserved. `cleanup-orphaned` is dry-run unless `--confirm`.
|
|
77
|
+
- Since the terminal-state fix (the job registry's `onTerminal` hook), a job
|
|
78
|
+
that terminally fails now propagates to its `import_runs` row automatically —
|
|
79
|
+
**new** orphans shouldn't appear; `cleanup-orphaned` is for pre-fix strays and
|
|
80
|
+
as a backstop.
|
|
81
|
+
- The **reaper** only touches the generic `jobs` table (frees leases / fails
|
|
82
|
+
stalled jobs); it does **not** scan `import_runs`. A "running" import with all
|
|
83
|
+
workers idle = an orphaned domain row → use `cleanup-orphaned`.
|
|
84
|
+
- `jobs list --kind` filters client-side after paging; raise `--limit` if you page
|
|
85
|
+
past the row you want.
|
|
86
|
+
|
|
87
|
+
## Related
|
|
88
|
+
- [Verify that a deploy's migrations ran](./verify-migrations.md)
|
|
89
|
+
- Command reference: `plexus-cli jobs list --help`, `plexus-cli workers reap --help`,
|
|
90
|
+
`plexus-cli imports cleanup-orphaned --help`.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Verify that a deploy's migrations ran
|
|
2
|
+
|
|
3
|
+
**When to use:** after a staging or production deploy whose release lists
|
|
4
|
+
pending migrations (the release's `PENDING_MIGRATIONS` list), when you need the
|
|
5
|
+
state from the deployment itself rather than from the deploy log. The deploy job
|
|
6
|
+
already prints, by name, the dry run of the next migration, every migration that
|
|
7
|
+
ran (with its document count) and a final check that every pending entry is
|
|
8
|
+
`success` — it goes red if any is not; that log is the platform operator's.
|
|
9
|
+
|
|
10
|
+
## Prerequisites
|
|
11
|
+
- `--env staging|prod` (or `local` for a local development backend).
|
|
12
|
+
- The CLI token's user holds `_platform.config.read` (every full platform
|
|
13
|
+
admin does). Anyone else gets `FORBIDDEN`.
|
|
14
|
+
|
|
15
|
+
## Steps
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
plexus-cli --env staging migrations status # pending migrations, exit 1 while one is not success
|
|
19
|
+
plexus-cli --env staging migrations status --recent # + the 20 most recently started of any name
|
|
20
|
+
plexus-cli --env staging migrations status --json # raw answer + `unfinished`
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
A release script gates on the exit code instead of grepping the output:
|
|
24
|
+
`plexus-cli --env prod migrations status || exit 1`.
|
|
25
|
+
|
|
26
|
+
Underneath it reads `migrations:getStatus`, which returns two lists of `{ name, state, isDone, processed, error, latestStart, latestEnd }`:
|
|
27
|
+
- **`pending`**: every entry of `PENDING_MIGRATIONS`, in run order. After a good
|
|
28
|
+
deploy each one reads `state: "success"`, `isDone: true`. A name that reads
|
|
29
|
+
`unknown` never started on this deployment.
|
|
30
|
+
- **`recent`**: the 20 most recently started migrations of any name, newest
|
|
31
|
+
first. Use it to see what the last deploy actually touched.
|
|
32
|
+
|
|
33
|
+
## Verify
|
|
34
|
+
- Every `pending` entry is `success`, and `latestEnd` is after the deploy's
|
|
35
|
+
workflow run started.
|
|
36
|
+
- `pending` is empty when the release carries no migration. Then there is
|
|
37
|
+
nothing to verify, and the migration step only pushed functions.
|
|
38
|
+
|
|
39
|
+
## Gotchas
|
|
40
|
+
- `failed` or `canceled` with an `error`: `runPending` threw and the deploy
|
|
41
|
+
stopped before the config bootstrap. Read the error here; the host-side output
|
|
42
|
+
is in the deploy log (platform operator).
|
|
43
|
+
- `inProgress` long after the deploy means the migration is stuck. Hand it to a
|
|
44
|
+
platform operator; do not try to restart it from the CLI.
|
|
45
|
+
- A local development backend lists every pending migration as `unknown` (exit 1): the seed
|
|
46
|
+
builds data in the new shape and never runs migrations. Only staging/prod answer the question.
|
|
47
|
+
- „Could not find public function migrations:getStatus“: the CLI points at a deployment
|
|
48
|
+
without this release (often a local development backend that never received the
|
|
49
|
+
functions). Check `--env` (`plexus-cli config show`).
|
|
50
|
+
- The query shows names, state, progress and errors only; it never returns a
|
|
51
|
+
cursor or document data.
|