@smartkomm/plexus-cli 0.0.0-stage → 0.1.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.
- package/README.md +826 -2
- package/dist/cli.mjs +483 -0
- package/dist/skill/.managed-by-plexus-cli +1 -0
- package/dist/skill/SKILL.md +185 -0
- package/dist/skill/references/accounts/email-changes.md +46 -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 +170 -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/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 +159 -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 +59 -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 +145815 -0
- package/package.json +53 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
b0828b779f68e43815116c5103ec6d7e6d29bd7aae08a1da060ceda112fc809a
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: plexus-cli
|
|
3
|
+
description: "Cli: drives a school's Plexus tenant (school administration — admissions, forms, workflows, letters, contracts, users) from the shell with `plexus-cli`, as the person whose API token it uses. Use when a Plexus tenant must be inspected or set up from the terminal: an admission offer (form, workflow, capacities), a letter's wording, test applications, users, exports — or when plexus-cli is named. Every task has a recipe under references/; each recipe states whether a school's tenant token suffices or a platform operator is needed."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# plexus-cli
|
|
7
|
+
|
|
8
|
+
`plexus-cli` operates a Plexus tenant through the platform's public API. Every
|
|
9
|
+
call runs as the person who minted the token, with exactly that person's rights —
|
|
10
|
+
the CLI adds no authority of its own. A school's agent works with a **tenant
|
|
11
|
+
token**: what the school's admin may do in the app, the CLI may do; everything
|
|
12
|
+
else the platform operator does.
|
|
13
|
+
|
|
14
|
+
## Connect
|
|
15
|
+
|
|
16
|
+
1. In the app: **Einstellungen → API-Token**, create a token (`plx_…`). It carries
|
|
17
|
+
your rights in every tenant you can reach; keep it like a password. Every call
|
|
18
|
+
checks the right to manage API tokens again; the change that takes it away
|
|
19
|
+
revokes the token (401 „… wurde am <Datum> widerrufen“) — mint a new one.
|
|
20
|
+
2. Configure the environment once, either in `~/.config/agent-tools/plexus.yml`:
|
|
21
|
+
|
|
22
|
+
```yaml
|
|
23
|
+
plexus:
|
|
24
|
+
default_tenant: <slug>
|
|
25
|
+
environments:
|
|
26
|
+
prod: { convex_url: "https://db.plexus.swop.schule", site_url: "https://actions.db.plexus.swop.schule", token: "plx_…" }
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
or as environment variables `PLEXUS_ENV`, `PLEXUS_CONVEX_URL`, `PLEXUS_SITE_URL`,
|
|
30
|
+
`PLEXUS_TOKEN`, `PLEXUS_TENANT`.
|
|
31
|
+
3. Check: `plexus-cli config show` (resolved connection, no network) and
|
|
32
|
+
`plexus-cli --env prod whoami` (who the token is).
|
|
33
|
+
|
|
34
|
+
## Rules that always hold
|
|
35
|
+
|
|
36
|
+
- **Name the environment on every write.** A writing command refuses without
|
|
37
|
+
`--env` on the command line, and production additionally needs `--allow-prod`.
|
|
38
|
+
- **Plan before apply.** Every declarative change has a dry run — `offer plan`,
|
|
39
|
+
`catalog plan`, `config apply` without `--confirm`, a command's own `--dry-run`.
|
|
40
|
+
Read it, then apply the same input. Never apply what you did not plan.
|
|
41
|
+
- **Export, edit, re-import — never retype live state.** `offer export` /
|
|
42
|
+
`config export` into a private scratch directory, edit there, `plan`, `apply`.
|
|
43
|
+
The live tenant is the source of truth; a file you keep is a copy.
|
|
44
|
+
- **Fail loud means stop.** A refusal names its reason (`[area/code] …`); fix the
|
|
45
|
+
input. Do not work around it with `call`.
|
|
46
|
+
- **Unsure what a command takes?** `plexus-cli <command> --help`; the callable
|
|
47
|
+
backend surface: `plexus-cli functions`, `plexus-cli describe <fn>`.
|
|
48
|
+
|
|
49
|
+
## What a tenant token can do — and what it cannot
|
|
50
|
+
|
|
51
|
+
A school's admin token reaches its own tenant and no other: admission offers
|
|
52
|
+
(forms, workflows, tasks, letters bound to nodes, capacities, publishing), letter
|
|
53
|
+
wording and the letter's salutation and sign-off, test applications, contract
|
|
54
|
+
grants, the billing catalog, exports, prospects, and the tenant config of its own
|
|
55
|
+
tenant — `config export`, `config targets`, `config set`, and `config apply
|
|
56
|
+
--from snapshot:…` or `--from env:…`. Every config write needs the config right
|
|
57
|
+
held tenant-wide. A change to the permission groups also needs the right to
|
|
58
|
+
manage permissions, and a change to who sits in which group needs the right to
|
|
59
|
+
manage users. The tenant admin holds all three.
|
|
60
|
+
|
|
61
|
+
A tenant-scoped `config apply` or `config set` refuses (`FORBIDDEN`, naming the
|
|
62
|
+
field) when the change would:
|
|
63
|
+
|
|
64
|
+
- create a tenant, apply the built-in blueprint (the default without `--from`),
|
|
65
|
+
or delete anything (`--allow-destructive`);
|
|
66
|
+
- change a `tenant.*` setting other than `branding`, `brandingAssets` and
|
|
67
|
+
`notificationChrome`, for example modules, the default applicant group, the mail
|
|
68
|
+
level, sign-in by link, or the terms;
|
|
69
|
+
- change the import `source`;
|
|
70
|
+
- name a user in `foundation.users` who is not already an active member of
|
|
71
|
+
this tenant with a login, so it never creates an account;
|
|
72
|
+
- put a `_platform.*` right into a permission group.
|
|
73
|
+
|
|
74
|
+
Copy those values unchanged from `config export`. Changing them is the operator's
|
|
75
|
+
job.
|
|
76
|
+
|
|
77
|
+
These need the **platform operator** (a `_platform.*` right). Ask them; do not
|
|
78
|
+
try: creating, onboarding or wiping a tenant (`config wipe`, `config prune-offers`,
|
|
79
|
+
`config apply --scope platform`), `offer hard-delete`, the settings listed above,
|
|
80
|
+
the `branding …` and `files put|remove` commands (a school sets its branding and
|
|
81
|
+
library files through `config apply` or in the app), issuing a token for someone
|
|
82
|
+
else (`tokens create --for`), every `users` command except `users groups`
|
|
83
|
+
(the school changes memberships in the app or through `foundation.users`), the mail
|
|
84
|
+
delivery monitor (`notifications list|show|retry|self-test`), Stripe Connect and
|
|
85
|
+
e-signature connections, contract resolvers (contract logic is platform code),
|
|
86
|
+
retention runs, feedback triage, worker jobs and migrations. A command without
|
|
87
|
+
the right answers with a refusal. It never makes a partial change.
|
|
88
|
+
|
|
89
|
+
## Setting up a school
|
|
90
|
+
|
|
91
|
+
The usual order, each step its recipe below:
|
|
92
|
+
|
|
93
|
+
1. The operator creates the tenant, its config and the first admin
|
|
94
|
+
(`config/onboard-tenant.md`, `users/add-tenant-admin.md`) — Plattform.
|
|
95
|
+
2. Offers: create the pedagogy unit and the offer, author the form, attach it to
|
|
96
|
+
the workflow, add the steps (decision, documents, tasks), set capacities —
|
|
97
|
+
`offer/…`. Validate offline with `offer validate --dir <bundle>` at any time.
|
|
98
|
+
3. Letters: reword what the school sends (`notifications/reword-a-letter.md`).
|
|
99
|
+
4. Try it: start and drive a test application
|
|
100
|
+
(`applications/start-a-test-application.md`, `test-actions/drive-a-test-application.md`).
|
|
101
|
+
5. Publish (`offer/publish-offer-bundle.md`) — `offer plan` first.
|
|
102
|
+
|
|
103
|
+
## References
|
|
104
|
+
|
|
105
|
+
Recht: **Mandant** = a tenant admin's token suffices for every step ·
|
|
106
|
+
**Mandant + Plattform** = the core works with the tenant token; the steps the recipe
|
|
107
|
+
names as operator steps need the platform operator · **Plattform** = the recipe as a
|
|
108
|
+
whole is the operator's (ask them; do not try).
|
|
109
|
+
|
|
110
|
+
| Recipe | Recht | Purpose |
|
|
111
|
+
|---|---|---|
|
|
112
|
+
| [accounts/email-changes.md](references/accounts/email-changes.md) | Mandant + Plattform | See who changed an account's sign-in address (the tenant-wide view needs a platform admin for --user). |
|
|
113
|
+
| [applications/inspect-process-store.md](references/applications/inspect-process-store.md) | Mandant | Show what an application's process produced and what it still waits for. |
|
|
114
|
+
| [applications/start-a-test-application.md](references/applications/start-a-test-application.md) | Mandant | Start a test application in the terminal, drive it and discard it. |
|
|
115
|
+
| [audit/answer-access-request.md](references/audit/answer-access-request.md) | Plattform | Answer an Art. 15 request: who looked at this application? |
|
|
116
|
+
| [config/attach-a-template.md](references/config/attach-a-template.md) | Mandant + Plattform | Put a template into the file library and attach it to a form field (`files put` and `files remove` need the operator; a school adds the file through `config apply` or the app). |
|
|
117
|
+
| [config/init-tenant.md](references/config/init-tenant.md) | Plattform | Generate and apply a minimal valid tenant config without an existing source. |
|
|
118
|
+
| [config/onboard-tenant.md](references/config/onboard-tenant.md) | Plattform | Set up or refresh a real tenant's config from a snapshot or another environment. |
|
|
119
|
+
| [config/rename-program-stage.md](references/config/rename-program-stage.md) | Mandant | Rename, add, reorder or retire a grade without losing applications. |
|
|
120
|
+
| [config/set-tenant-branding.md](references/config/set-tenant-branding.md) | Mandant + Plattform | Give the application portal colours, radius and fonts; upload logo and WOFF2 fonts (the `branding …` commands need the operator; a school uses `config apply`). |
|
|
121
|
+
| [config/set-tenant-terms.md](references/config/set-tenant-terms.md) | Plattform | Switch a school from „Bewerbung" to „Anmeldung" throughout with one setting. |
|
|
122
|
+
| [config/set-up-billing-catalog.md](references/config/set-up-billing-catalog.md) | Mandant + Plattform | Load articles, packages, reduction tiers and Word templates by stable keys (catalog plan/apply). |
|
|
123
|
+
| [config/update-tenant-config.md](references/config/update-tenant-config.md) | Mandant | Change the live config by export → edit → dry run → confirm. |
|
|
124
|
+
| [config/wipe-tenant-config.md](references/config/wipe-tenant-config.md) | Plattform | Empty one config target, optionally with dependent data, or take a tenant down. |
|
|
125
|
+
| [export/create-named-export.md](references/export/create-named-export.md) | Mandant | Create a reusable XLSX person export template. |
|
|
126
|
+
| [feedback/create-and-triage-feedback.md](references/feedback/create-and-triage-feedback.md) | Plattform | File platform feedback, assign it, list it, and file it as a GitHub issue with its screenshot. |
|
|
127
|
+
| [feedback/manage-feedback-tags.md](references/feedback/manage-feedback-tags.md) | Plattform | Maintain the feedback tag catalogue and tag many items at once. |
|
|
128
|
+
| [finance/change-contract-grant.md](references/finance/change-contract-grant.md) | Mandant | Change a running contract's grants or reductions from a month on. |
|
|
129
|
+
| [finance/record-submission-date.md](references/finance/record-submission-date.md) | Mandant | Record a missing historic submission date of a SEPA direct debit. |
|
|
130
|
+
| [interessenten/list-interessenten.md](references/interessenten/list-interessenten.md) | Mandant | List a tenant's prospects. |
|
|
131
|
+
| [notifications/debug-mail-delivery.md](references/notifications/debug-mail-delivery.md) | Plattform | Clear up a failed mail delivery: status, error code, self-test, retry. |
|
|
132
|
+
| [notifications/mail-level.md](references/notifications/mail-level.md) | Plattform | Set whether a school sends families only sign-in links or all mail. |
|
|
133
|
+
| [notifications/reword-a-letter.md](references/notifications/reword-a-letter.md) | Mandant + Plattform | Reword a school's letter per language and check the rendered letter. |
|
|
134
|
+
| [notifications/trace-a-login.md](references/notifications/trace-a-login.md) | Plattform | Trace why someone could not sign in. |
|
|
135
|
+
| [offer/add-workflow-node.md](references/offer/add-workflow-node.md) | Mandant | Insert a node (approval, form, contract, collectPayment, end …) into the workflow. |
|
|
136
|
+
| [offer/addresses.md](references/offer/addresses.md) | Mandant | How a node reads by full address ($node…, $task…, $derived…, $env…), which flag sets which field, and the gate codes. |
|
|
137
|
+
| [offer/appointment-task.md](references/offer/appointment-task.md) | Mandant | Give a booked appointment its appointment task (held by the host, due N days after the end). |
|
|
138
|
+
| [offer/assignment-rule.md](references/offer/assignment-rule.md) | Mandant + Plattform | Change, test and publish who is responsible per slot; replace a person who leaves (`users unset-group` needs the operator; a school removes the membership in the app). |
|
|
139
|
+
| [offer/attach-form-to-workflow.md](references/offer/attach-form-to-workflow.md) | Mandant | Bind an existing or new SmartForm to a form node. |
|
|
140
|
+
| [offer/author-form-scenarios.md](references/offer/author-form-scenarios.md) | Mandant | Add a form's test scenarios and $env variables. |
|
|
141
|
+
| [offer/author-manifest-meta.md](references/offer/author-manifest-meta.md) | Mandant | Set the manifest settings, role instances and services of an offer. |
|
|
142
|
+
| [offer/author-smartform.md](references/offer/author-smartform.md) | Mandant | Build a SmartForm from scratch with the CLI and publish it. |
|
|
143
|
+
| [offer/bind-node-input.md](references/offer/bind-node-input.md) | Mandant | Bind a node's input slot to an address, a literal or the default. |
|
|
144
|
+
| [offer/completeness-check.md](references/offer/completeness-check.md) | Mandant | Wait for outstanding documents, with a deadline and a guest role. |
|
|
145
|
+
| [offer/countersign-node.md](references/offer/countersign-node.md) | Mandant + Plattform | Add the school's countersignature of the contract (needs a sealed contract resolver). |
|
|
146
|
+
| [offer/create-pedagogy-unit.md](references/offer/create-pedagogy-unit.md) | Mandant | Make a unit something families can apply to. |
|
|
147
|
+
| [offer/declare-node-reads.md](references/offer/declare-node-reads.md) | Mandant + Plattform | Declare which form fields a document node reads and locks. |
|
|
148
|
+
| [offer/edit-live-tenant-offer.md](references/offer/edit-live-tenant-offer.md) | Mandant + Plattform | Change a live offer: fresh export → change → plan → apply → plan shows all same (resolver and signing steps need the operator). |
|
|
149
|
+
| [offer/edit-smartform.md](references/offer/edit-smartform.md) | Mandant | Change a SmartForm in detail (properties, order, English texts). |
|
|
150
|
+
| [offer/edit-workflow-node.md](references/offer/edit-workflow-node.md) | Mandant | Rename, rewire, rebind or reroute a node. |
|
|
151
|
+
| [offer/engage-push-node.md](references/offer/engage-push-node.md) | Mandant + Plattform | Insert the Engage transfer node that pushes an accepted application to Engage SMIS once. |
|
|
152
|
+
| [offer/insert-and-rewire-node.md](references/offer/insert-and-rewire-node.md) | Mandant | Put a step onto exactly one edge and onto an existing board state. |
|
|
153
|
+
| [offer/node-tasks.md](references/offer/node-tasks.md) | Mandant | Add, change or remove the tasks a node spawns: result kind, blockers, condition, evidence. |
|
|
154
|
+
| [offer/optional-assessment.md](references/offer/optional-assessment.md) | Mandant | An optional assessment with files and an „Einschätzung einholen“ action. |
|
|
155
|
+
| [offer/publish-offer-bundle.md](references/offer/publish-offer-bundle.md) | Mandant | Publish a bundle or stage it internally, and verify it is really live. |
|
|
156
|
+
| [offer/set-offer-capacities.md](references/offer/set-offer-capacities.md) | Mandant | Maintain seat quotas per school year × grade in bundle.yaml. |
|
|
157
|
+
| [offer/staff-input-if-node.md](references/offer/staff-input-if-node.md) | Mandant | A staff input plus an if node that branches on it. |
|
|
158
|
+
| [offer/task-appointment-result.md](references/offer/task-appointment-result.md) | Mandant | A task that ends with an appointment (mail with .ics to the family). |
|
|
159
|
+
| [offer/task-family-request.md](references/offer/task-family-request.md) | Mandant | A task asks the family for data and closes itself on the answer. |
|
|
160
|
+
| [offer/task-result-files.md](references/offer/task-result-files.md) | Mandant | A task result with up to 20 files. |
|
|
161
|
+
| [offer/template-document-node.md](references/offer/template-document-node.md) | Mandant | Produce a letter from a Word template with a template node. |
|
|
162
|
+
| [offer/workflow-default-channels.md](references/offer/workflow-default-channels.md) | Mandant | Fixed team chat channels for every application of an offer. |
|
|
163
|
+
| [resolver/manage-contract-resolver.md](references/resolver/manage-contract-resolver.md) | Plattform | Create, test, seal, assign and revise a contract resolver. |
|
|
164
|
+
| [retention/erase-due-applications.md](references/retention/erase-due-applications.md) | Plattform | Erase applications past their retention period (retention plan/apply). |
|
|
165
|
+
| [retention/erase-due-parties.md](references/retention/erase-due-parties.md) | Plattform | Hard-delete persons the school's deletion rule has reached (retention parties plan/apply). |
|
|
166
|
+
| [roles/migrate-parent-assignments.md](references/roles/migrate-parent-assignments.md) | Mandant | Move direct assignments from a parent role onto a leaf role. |
|
|
167
|
+
| [signing/README.md](references/signing/README.md) | Mandant | Send a contract revision for e-signature, reconcile, withdraw, remind (connecting the provider is Plattform). |
|
|
168
|
+
| [signing/connect-a-school.md](references/signing/connect-a-school.md) | Plattform | Connect a school to its own DocuSign or inSign account. |
|
|
169
|
+
| [signing/rotate-the-key.md](references/signing/rotate-the-key.md) | Plattform | Rotate the signing credentials key and reseal every connection. |
|
|
170
|
+
| [stammdaten/purge-deleted-person.md](references/stammdaten/purge-deleted-person.md) | Plattform | Delete a soft-deleted person for good. |
|
|
171
|
+
| [stripe/connect-a-school.md](references/stripe/connect-a-school.md) | Plattform | Connect a school to its own Stripe account. |
|
|
172
|
+
| [stripe/request-and-verify-a-payment.md](references/stripe/request-and-verify-a-payment.md) | Mandant | Request a Stripe payment through call and follow it to succeeded (needs a connected school). |
|
|
173
|
+
| [test-actions/drive-a-test-application.md](references/test-actions/drive-a-test-application.md) | Mandant | Play a test application through with test-mode actions. |
|
|
174
|
+
| [users/add-platform-admin.md](references/users/add-platform-admin.md) | Plattform | Create a tenant-less platform admin with a login. |
|
|
175
|
+
| [users/add-platform-user.md](references/users/add-platform-user.md) | Plattform | Create a platform user with limited _platform.* rights. |
|
|
176
|
+
| [users/add-tenant-admin.md](references/users/add-tenant-admin.md) | Plattform | Create a ready admin login for a tenant and hand over the credentials. |
|
|
177
|
+
| [users/change-login-email.md](references/users/change-login-email.md) | Plattform | Change an account's sign-in address. |
|
|
178
|
+
| [users/cli-access-for-tenant-admin.md](references/users/cli-access-for-tenant-admin.md) | Mandant + Plattform | Give a school's administrator a CLI token for exactly their tenant (minting it themselves suffices; issuing it for them with `tokens create --for` needs the operator). |
|
|
179
|
+
| [users/delete-permission-group.md](references/users/delete-permission-group.md) | Mandant | Delete an orphaned permission group. |
|
|
180
|
+
| [users/grant-cross-tenant-access.md](references/users/grant-cross-tenant-access.md) | Plattform | Give every user of every tenant access to the test tenant. |
|
|
181
|
+
| [users/grant-feedback-assignable.md](references/users/grant-feedback-assignable.md) | Plattform | Make a platform admin assignable for feedback. |
|
|
182
|
+
| [users/set-password.md](references/users/set-password.md) | Plattform | Set or regenerate an existing user's password. |
|
|
183
|
+
| [users/use-test-account.md](references/users/use-test-account.md) | Mandant + Plattform | Play a school's application process as an applicant with a test account. |
|
|
184
|
+
| [worker-ops/debug-worker-jobs.md](references/worker-ops/debug-worker-jobs.md) | Plattform | Investigate and clean up stuck or failed imports and worker jobs. |
|
|
185
|
+
| [worker-ops/verify-migrations.md](references/worker-ops/verify-migrations.md) | Plattform | Check after a deploy that the migrations ran (migrations status). |
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Read who changed a login address
|
|
2
|
+
|
|
3
|
+
**When to use:** a school asks „wer hat die Anmelde-Adresse dieser Familie
|
|
4
|
+
geändert?", a family says „ich bekomme keine Links mehr", a suspected takeover
|
|
5
|
+
(someone signed in on an address staff set), or a data-subject request (Art. 15)
|
|
6
|
+
about an address.
|
|
7
|
+
|
|
8
|
+
Every change of a login address by someone other than the holder — staff in the
|
|
9
|
+
Stammdaten, the platform operator — is one row in `account_email_changes`:
|
|
10
|
+
when, which account, from → to, who, whether the old address was proven, and
|
|
11
|
+
when the first session on the new address began. Read-only; the command never
|
|
12
|
+
writes and needs no `--allow-prod`.
|
|
13
|
+
|
|
14
|
+
## Steps
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# 1. The school's own review: every change its staff made, newest first.
|
|
18
|
+
# Needs administration.users.manage in the tenant (a platform admin has it).
|
|
19
|
+
plexus-cli --env <env> --tenant <slug> accounts email-changes
|
|
20
|
+
|
|
21
|
+
# 2. Everything about ONE address — current, previous or new, all tenants,
|
|
22
|
+
# the operator's changes included (platform-admin). The data-subject view:
|
|
23
|
+
# the employee is shown as „Schule" / „Plattformbetrieb" — named only for an
|
|
24
|
+
# operator holding the opt-in right _platform.audit.readActor (misuse check).
|
|
25
|
+
plexus-cli --env <env> accounts email-changes --user <email>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Columns: `at` · `account` (the address NOW; „(gelöscht)" when the account is
|
|
29
|
+
gone, „(kein Zugang mehr)" in a school's view of an account that left it, masked
|
|
30
|
+
after erasure) · `from` · `to` · `by` · `oldProven` · `firstSession` (the first sign-in
|
|
31
|
+
after the change, `—` if none yet) · `redacted` (`ja` = the account's last
|
|
32
|
+
application in that school was erased, or the change is older than 180 days;
|
|
33
|
+
addresses are masked, the rest is kept).
|
|
34
|
+
|
|
35
|
+
## Verify
|
|
36
|
+
The same changes are on the application timeline as „Anmelde-Adresse geändert"
|
|
37
|
+
and „Erste Anmeldung nach Adressänderung" (cockpit → Aktivitäten), and in the
|
|
38
|
+
UI: Administration → „Anmelde-Adressen" (school), Plattform → Benutzer (all).
|
|
39
|
+
|
|
40
|
+
## Gotchas
|
|
41
|
+
- Only third-party changes are rows. A visitor adopting the address they typed,
|
|
42
|
+
or a staff member changing their OWN address, leaves none.
|
|
43
|
+
- An operator change has no tenant: step 1 does not show it, step 2 does.
|
|
44
|
+
- After masking (erasure, or 180 days) the addresses are `f…@…`, so `--user`
|
|
45
|
+
with the OLD address no longer finds the row; the account's current address
|
|
46
|
+
still does.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Inspect an application's process store
|
|
2
|
+
|
|
3
|
+
**When to use:** see what the process of ONE application has produced and what it is still
|
|
4
|
+
waiting for — a form instance, a document, a contract with its current revision, a payment,
|
|
5
|
+
a value a task wrote (`$task.<task>.<key>`), a derived price (`$derived.<key>`) and the
|
|
6
|
+
application's facts (`$env.applicant.language`). Typical questions: why a contract input is
|
|
7
|
+
empty, why a price „wartet", which revision a contract node points at, whether a task value
|
|
8
|
+
counts after a re-open. Replaces raw `call processStoreView:get`.
|
|
9
|
+
|
|
10
|
+
## Prerequisites
|
|
11
|
+
- `--tenant`; the token's user holds `bewerbungsmanagement.applications.manage` (the query is
|
|
12
|
+
staff-only: it shows every value whatever its `audience`).
|
|
13
|
+
- The application id (`applications list --offer <slug>`).
|
|
14
|
+
- Read-only: no `--env` guard, works against prod with the usual token.
|
|
15
|
+
|
|
16
|
+
## Steps
|
|
17
|
+
```bash
|
|
18
|
+
E="--env staging --tenant test"
|
|
19
|
+
|
|
20
|
+
# One row per address: state, what it holds, producing node, when written.
|
|
21
|
+
plexus-cli $E applications store <applicationId>
|
|
22
|
+
|
|
23
|
+
# The same as JSON, keyed by the canonical address — the shape the cockpit's
|
|
24
|
+
# „Prozessdaten" block (Graph tab, manage grant) shows.
|
|
25
|
+
plexus-cli $E applications store <applicationId> --json
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Reading it
|
|
29
|
+
- **Every address of the PINNED graph** is listed — the application's own version, not the
|
|
30
|
+
live head: `$node.<node>.<key>` for each node result, `$task.<task>.<key>` for each value a
|
|
31
|
+
task writes, `$derived.<key>` for each derived price, then `$env.*`.
|
|
32
|
+
- **State is resolved, never the newest row:**
|
|
33
|
+
- `present` — the producer's result; for a task value the entry of the task row that is
|
|
34
|
+
currently effective (`writtenByTaskId`, `writtenAt`); for a price the computed value.
|
|
35
|
+
- `pending` — not there yet. A task value or a price names the reason („… ist noch offen",
|
|
36
|
+
„… ist noch nicht erreicht"); a `$node` result that its node has not written yet has none.
|
|
37
|
+
A form instance in `draft` is `present` with `state: draft`.
|
|
38
|
+
- `absent` — none is coming: the writing task is not owed (its condition is false), or the
|
|
39
|
+
application has no applicant account (`$env.applicant.language`).
|
|
40
|
+
- **Refs are resolved live:** `form` → instance and its state; `document` → the slot and its
|
|
41
|
+
newest file; `contract` → contract id, current revision (`revisionNumber`) and
|
|
42
|
+
`contractStatus` (`signed` / `inSignature` / `withdrawn` / `draft` / `active`, derived in that precedence).
|
|
43
|
+
|
|
44
|
+
## Verify
|
|
45
|
+
- A completed store-writing task shows `present` with its `writtenByTaskId`; re-open it and
|
|
46
|
+
the address turns `pending` — the old entry stays as history but no longer counts.
|
|
47
|
+
|
|
48
|
+
## Gotchas
|
|
49
|
+
- **Never hand the output to the family.** It carries internal values (a need-based tier,
|
|
50
|
+
a reduction) that no family surface shows.
|
|
51
|
+
- **A redacted application is refused** („Die Daten dieser Bewerbung wurden gelöscht.") —
|
|
52
|
+
its process data are deleted; the view never answers with an empty store.
|
|
53
|
+
- **Texts are German** (reasons, producer labels) — they are the resolvers' own texts.
|
|
54
|
+
- **Forbidden** for a member with only `applications.read`: the view needs the manage grant.
|
|
55
|
+
- A `bug` naming `[process]` (a ref whose target is gone, a done task without its entry) is a
|
|
56
|
+
data defect, not a display problem — report it to the platform operator with the
|
|
57
|
+
application id and the address.
|
|
58
|
+
|
|
59
|
+
## Related
|
|
60
|
+
- [Start a test application and drive it end to end](./start-a-test-application.md)
|
|
61
|
+
- [Addresses in a workflow](../offer/addresses.md) — what the addresses mean and who may read them
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Start a test application and drive it end to end
|
|
2
|
+
|
|
3
|
+
**When to use:** check an offer from the terminal the way a family would go through it —
|
|
4
|
+
start a test application, load a form scenario and submit, run the next step's actions,
|
|
5
|
+
look at the result, throw the application away. Replaces raw `call
|
|
6
|
+
admission:staffStartApplicationForParty` / `workflow:runTestAction` / `admission:discardTest`.
|
|
7
|
+
|
|
8
|
+
## Prerequisites
|
|
9
|
+
- `--env` named on the command line for every writing verb (`start-test`, `discard-test`,
|
|
10
|
+
`test-actions run`) — the configured default is not enough. Prod additionally needs
|
|
11
|
+
`--allow-prod`, and there a test application starts only for the tenant's test account
|
|
12
|
+
(`test@example.com`, see [Use a tenant's test account](../users/use-test-account.md)).
|
|
13
|
+
- `--tenant`; the token's user holds `bewerbungsmanagement.applications.manage` and
|
|
14
|
+
`bewerbungsmanagement.testActions.run` — the server asks for both to start in test mode,
|
|
15
|
+
and the second for every test action.
|
|
16
|
+
- The applicant: an existing account of this school (`--applicant <login address>`) or its
|
|
17
|
+
party id (`--party`). Creating an account has no verb (it needs the cockpit's password flow).
|
|
18
|
+
- School year and level by **key or id** — never by name (`applications start-test` lists
|
|
19
|
+
both when one does not match).
|
|
20
|
+
|
|
21
|
+
## Steps
|
|
22
|
+
```bash
|
|
23
|
+
E="--env staging --tenant test"
|
|
24
|
+
|
|
25
|
+
# 1. Start (always test mode); prints the application id and what test mode offers now.
|
|
26
|
+
plexus-cli $E applications start-test --offer grundschule-havelland \
|
|
27
|
+
--run <runKey|runId> --stage klasse-1 --applicant test@example.com
|
|
28
|
+
# An offer that declares staff inputs needs each one, once:
|
|
29
|
+
# --staff-input staffApplication=false (see ../offer/staff-input-if-node.md)
|
|
30
|
+
|
|
31
|
+
# 2. Load a scenario and submit (file fields are stubbed first; runs asynchronously).
|
|
32
|
+
plexus-cli $E test-actions run --application <id> --action load-scenario:standard
|
|
33
|
+
|
|
34
|
+
# 3. See where it stands and what the next node offers; run an action there.
|
|
35
|
+
plexus-cli $E applications show <id>
|
|
36
|
+
plexus-cli $E test-actions list --application <id>
|
|
37
|
+
plexus-cli $E test-actions run --application <id> --action takeResponsibility
|
|
38
|
+
|
|
39
|
+
# 4. Throw it away.
|
|
40
|
+
plexus-cli $E applications discard-test <id>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Reading verbs (no `--env` needed beyond the usual resolution):
|
|
44
|
+
```bash
|
|
45
|
+
plexus-cli $E applications list --offer grundschule-havelland [--test-only]
|
|
46
|
+
plexus-cli $E applications counts --offer grundschule-havelland
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Verify
|
|
50
|
+
- `applications show <id>` prints `test: ja`, the current nodes and the status.
|
|
51
|
+
- After step 2, `test-actions list` shows the next node's actions (the submit lands a moment
|
|
52
|
+
after `run` returns — file fills are a scheduled action).
|
|
53
|
+
- After step 4, `applications list --offer … --test-only` no longer lists it.
|
|
54
|
+
|
|
55
|
+
## Idempotency / gotchas
|
|
56
|
+
- `start-test` creates a new application on every call; `discard-test` on a real application
|
|
57
|
+
is refused by the server.
|
|
58
|
+
- `--run`/`--stage` match a key or an id only; a display name is refused with the list of
|
|
59
|
+
what exists. A closed school year is refused.
|
|
60
|
+
- Without `--version` the application runs on the offer's live generation (as in the cockpit).
|
|
61
|
+
- On prod: `--allow-prod`, and only `--applicant test@example.com` — a test run must never land
|
|
62
|
+
in a real family's portal.
|
|
63
|
+
|
|
64
|
+
## Related
|
|
65
|
+
- [Drive a test application through test mode](../test-actions/drive-a-test-application.md) ·
|
|
66
|
+
[Author a form's test scenarios](../offer/author-form-scenarios.md)
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Answer a data-subject access request: who viewed this application?
|
|
2
|
+
|
|
3
|
+
**When to use:** an applicant (or their parent) asks *"who looked at my child's
|
|
4
|
+
application, and when?"* — an Art. 15 DSGVO request — and you must produce the
|
|
5
|
+
privileged-access report for one application. Also the starting point for an
|
|
6
|
+
anlassbezogene misuse check ("what did this employee open?").
|
|
7
|
+
|
|
8
|
+
> The report answers **"who opened the applicant-portal view of this application"**,
|
|
9
|
+
> plus platform-operator tenant entries and reads of the log itself. It is **not** a
|
|
10
|
+
> complete record of everyone who read the data — ordinary cockpit reads are
|
|
11
|
+
> deliberately not logged (a documented Abwägung in the platform's
|
|
12
|
+
> Protokollierungskonzept, § 3 — the platform operator provides it).
|
|
13
|
+
> Never phrase an answer as "nobody accessed your file" on the strength of an empty
|
|
14
|
+
> report.
|
|
15
|
+
|
|
16
|
+
## Prerequisites
|
|
17
|
+
- `plexus-cli --env staging|prod|local` configured with a **platform-operator** token;
|
|
18
|
+
the audit rights are `_platform.*`, so a tenant admin token cannot run this.
|
|
19
|
+
- `_platform.audit.read` — the default, identity-free report.
|
|
20
|
+
- `_platform.audit.readActor` — **only** for `--with-actor` / `--actor`. It is an
|
|
21
|
+
**opt-in** right, deliberately excluded from the default full-admin grant; grant it
|
|
22
|
+
with
|
|
23
|
+
`plexus-cli --env <env> users set-platform-permission <email> --permission _platform.audit.readActor --on`.
|
|
24
|
+
- The **application id** (`j…` Convex id). Two ways to get one:
|
|
25
|
+
- from the cockpit URL of the Vorgang, or
|
|
26
|
+
- `plexus-cli --env <env> call admission:list client=<slug> paginationOpts='{"numItems":25,"cursor":null}' --json`
|
|
27
|
+
(needs `bewerbungsmanagement.applications.read` in that tenant — a
|
|
28
|
+
`_platform.tenants.accessAll` operator has it).
|
|
29
|
+
|
|
30
|
+
## Steps
|
|
31
|
+
```bash
|
|
32
|
+
# 1. The answer the data subject is owed: date + purpose, NO employee named.
|
|
33
|
+
plexus-cli --env prod audit access --allow-prod --application <applicationId>
|
|
34
|
+
|
|
35
|
+
# 1a. Same report as machine-readable evidence for the file.
|
|
36
|
+
plexus-cli --env prod audit access --allow-prod --application <applicationId> --json
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Only where naming the employee is genuinely required (internal misuse check, works
|
|
40
|
+
council / DSB escalation — not the default answer to the applicant):
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# 2. Same rows, with the acting employee's email. Needs _platform.audit.readActor.
|
|
44
|
+
plexus-cli --env prod audit access --allow-prod --application <applicationId> --with-actor
|
|
45
|
+
|
|
46
|
+
# 3. The other direction: what did this employee access in this tenant?
|
|
47
|
+
plexus-cli --env prod audit access --allow-prod --actor <employee-email> --tenant <slug>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Also owed in the same answer — changes of the person's login address.**
|
|
51
|
+
If staff or the operator ever changed the address the person signs in with, that
|
|
52
|
+
is personal data about them too. It is read-only; unlike step 1 this read is not
|
|
53
|
+
recorded as `audit_read` (Protokollierungskonzept § 12):
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# 4. Every change naming the address the person gives you (current or old).
|
|
57
|
+
# The employee is shown as „Schule" / „Plattformbetrieb" — like step 1;
|
|
58
|
+
# named only with _platform.audit.readActor.
|
|
59
|
+
plexus-cli --env prod accounts email-changes --user <their-address>
|
|
60
|
+
```
|
|
61
|
+
Recipe and columns: [`accounts/email-changes.md`](../accounts/email-changes.md).
|
|
62
|
+
|
|
63
|
+
Output columns: `at` (ISO timestamp) · `purpose` (fixed German purpose text per
|
|
64
|
+
event type) · `actor` (only in the `readActor` projections).
|
|
65
|
+
|
|
66
|
+
## Verify
|
|
67
|
+
```bash
|
|
68
|
+
# The read you just performed is ITSELF logged as an `audit_read` row (M43.21.06).
|
|
69
|
+
# It shows up under YOUR account, not under the application:
|
|
70
|
+
plexus-cli --env prod audit access --allow-prod --actor <your-operator-email> --tenant <slug>
|
|
71
|
+
```
|
|
72
|
+
Expect one `audit_read` row ("Auskunft und Kontrolle: Einsicht in dieses
|
|
73
|
+
Zugriffsprotokoll") per report you ran. Needs `_platform.audit.readActor`; without it,
|
|
74
|
+
take the absence of an error on step 1 as the confirmation.
|
|
75
|
+
|
|
76
|
+
## Idempotency / gotchas
|
|
77
|
+
- **These commands WRITE.** Every projection is a Convex *mutation*, because each read
|
|
78
|
+
of the log records an `audit_read` row in the same transaction. Running the report
|
|
79
|
+
five times to "just check" leaves five rows on prod. It is not a read-only probe.
|
|
80
|
+
- **Pass exactly one of `--application` / `--actor`.** Passing both or neither fails
|
|
81
|
+
loud (`plexus audit access: pass exactly one of --application <id> or --actor <email>.`)
|
|
82
|
+
rather than silently preferring a mode — the two answer different questions and
|
|
83
|
+
carry different rights.
|
|
84
|
+
- **`--actor` is tenant-scoped and needs a tenant**: `--tenant <slug>`, else
|
|
85
|
+
the configured default tenant / `PLEXUS_TENANT`; without any of them it fails
|
|
86
|
+
loud. The underlying index is cross-tenant, so scoping is what stops one query from
|
|
87
|
+
returning every tenant's records.
|
|
88
|
+
- **An unknown `--actor` email throws** (`No Plexus identity for "…"`). It never
|
|
89
|
+
returns an empty list — "this person accessed nothing" is the one answer an
|
|
90
|
+
accountability search must not invent for an address it simply does not know.
|
|
91
|
+
- **The per-application report only ever contains portal views.** `operator_entry` and
|
|
92
|
+
`audit_read` rows carry no application by design; they surface only via `--actor`.
|
|
93
|
+
- **Truncation is announced, not hidden.** The report caps at 500 rows (the actor
|
|
94
|
+
search additionally scans at most 4 000 rows); a cut-off prints
|
|
95
|
+
`warning: the report was truncated — older rows are not shown.` to **stderr** (so it
|
|
96
|
+
never pollutes `--json` stdout). Treat a truncation warning as "this answer is
|
|
97
|
+
incomplete", not as a formatting detail.
|
|
98
|
+
- **Retention is 180 days.** Access rows older than that are gone, and all rows for an
|
|
99
|
+
application are deleted when that application is redacted/erased. An empty report may
|
|
100
|
+
simply mean the accesses aged out — say so in the answer instead of implying nobody
|
|
101
|
+
looked.
|
|
102
|
+
- **Do not hand `--with-actor` output to the data subject by default.** The identity of
|
|
103
|
+
the individual staff member is generally not part of what Art. 15 owes (CJEU
|
|
104
|
+
*Pankki S*, C-579/21); the default projection exists precisely so the standard answer
|
|
105
|
+
omits it. See § 7 of the Protokollierungskonzept before disclosing a name.
|
|
106
|
+
- On a local development deployment the seeded tenant (`test`) will normally have
|
|
107
|
+
no access rows at all.
|
|
108
|
+
|
|
109
|
+
## Related
|
|
110
|
+
- Concept + legal reasoning (the primary compliance artefact): the platform's
|
|
111
|
+
Protokollierungskonzept — ask the platform operator.
|
|
112
|
+
- Granting the opt-in actor right: [Add a platform user](../users/add-platform-user.md) ·
|
|
113
|
+
[Make a platform admin assignable to feedback](../users/grant-feedback-assignable.md)
|
|
114
|
+
(same `users set-platform-permission` mechanism).
|
|
115
|
+
- Command reference: `plexus-cli audit access --help`.
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Attach a blank template (Vorlage) to a form field
|
|
2
|
+
|
|
3
|
+
**When to use:** a school wants to offer a blank document on an upload field — a
|
|
4
|
+
reference form, a GDPR consent, a health record — that the family downloads,
|
|
5
|
+
fills in or signs, and uploads back at the same field. Or a template changed and
|
|
6
|
+
every form that offers it should show the new version.
|
|
7
|
+
|
|
8
|
+
The file lives in the tenant's **file library** (`tenant_files`, UI: Daten →
|
|
9
|
+
Dateien) under a stable **key**. A form field names the key only; label, file
|
|
10
|
+
name and type are read from the library. Replacing the file (same key) updates
|
|
11
|
+
every form at once — no republish.
|
|
12
|
+
|
|
13
|
+
## Prerequisites
|
|
14
|
+
|
|
15
|
+
- `plexus-cli` installed; a platform-admin token with `_platform.config.manage`
|
|
16
|
+
for the target env (listing needs `_platform.config.read`).
|
|
17
|
+
- The `files` commands and the `fileKey` attachment shape must be **live** on
|
|
18
|
+
the target env before an offer that uses them is published (a platform
|
|
19
|
+
operator rolls a release out to staging, then prod). An offer published before
|
|
20
|
+
that fails with `[config.attachment-source]` or `[form.unknown-attachment-file]`.
|
|
21
|
+
- Allowed types: PDF, DOCX, DOC, ODT, XLSX, JPG, PNG; max 20 MB.
|
|
22
|
+
|
|
23
|
+
## Steps
|
|
24
|
+
|
|
25
|
+
1. **Put the file into the library** (create — a new key needs `--label` AND
|
|
26
|
+
`--label-en`: the label is shown in the reader's language):
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
plexus-cli --env staging --tenant test files put referenzformular ./Referenzformular.pdf \
|
|
30
|
+
--label "Vorlage: Referenzformular" --label-en "Template: reference form"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`put` on an existing key **replaces** the file (keeps the key and the label
|
|
34
|
+
unless `--label` / `--label-en` is given). `--label-en` alone translates the
|
|
35
|
+
stored German; a German change over a real English is refused without
|
|
36
|
+
`--label-en`. Identical bytes and label are a no-op.
|
|
37
|
+
|
|
38
|
+
2. **Attach it to the field** in the offer bundle — a fresh export of the live
|
|
39
|
+
offer in a scratch directory of your own (production is the source of truth
|
|
40
|
+
for a real tenant's offer, so always start from a fresh export; see
|
|
41
|
+
[edit-live-tenant-offer](../offer/edit-live-tenant-offer.md)):
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
WORK=$(mktemp -d) # a scratch directory
|
|
45
|
+
plexus-cli --env prod --tenant <slug> offer export --slug <offer-slug> --out $WORK/<offer-slug>
|
|
46
|
+
plexus-cli offer form field attach --dir $WORK/<offer-slug> \
|
|
47
|
+
--key kind.referenz --file-key referenzformular
|
|
48
|
+
# an inline link in the label instead of a download chip:
|
|
49
|
+
# --render inlineLink --label "Referenzformular"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
3. **Validate and publish** the bundle as usual (`offer validate`, `offer plan`,
|
|
53
|
+
`offer apply --publish`). Publish checks the key against the target tenant's
|
|
54
|
+
library.
|
|
55
|
+
|
|
56
|
+
## Declarative path (config-as-code)
|
|
57
|
+
|
|
58
|
+
The library is part of the tenant config (`files`):
|
|
59
|
+
|
|
60
|
+
```jsonc
|
|
61
|
+
"files": [
|
|
62
|
+
{
|
|
63
|
+
"key": "referenzformular",
|
|
64
|
+
"label": { "de": "Vorlage: Referenzformular" },
|
|
65
|
+
"filename": "Referenzformular.pdf",
|
|
66
|
+
"mimeType": "application/pdf",
|
|
67
|
+
"sha256": "…", // recomputed from `path` by the CLI
|
|
68
|
+
"sizeBytes": 48213, // recomputed from `path` by the CLI
|
|
69
|
+
"path": "files/referenzformular/Referenzformular.pdf"
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
- `config export` downloads every file to `files/<key>/<filename>` beside
|
|
75
|
+
`config.json` and writes `path`. Swap a file on disk and `config apply --from
|
|
76
|
+
snapshot:…` uploads it (the file on disk decides the hash).
|
|
77
|
+
- `apply` uploads **only** files whose hash the target does not hold; a dry run
|
|
78
|
+
uploads nothing and lists them as `~ file files/<key>`.
|
|
79
|
+
- A file absent from `files` is reported as an **orphan** (`- orphan
|
|
80
|
+
foundation/tenant_files`) and removed only with `--allow-destructive` — then
|
|
81
|
+
still refused with `[file/inUse]` while any form version (archived included)
|
|
82
|
+
names it. A file uploaded on the Daten page is therefore never lost to an
|
|
83
|
+
older snapshot.
|
|
84
|
+
- A dry run also names changed files whose bytes the source cannot supply
|
|
85
|
+
(`NO bytes in this source`) — the commit would fail `[file/contentMissing]`.
|
|
86
|
+
- `--from env:<src>` downloads changed files from the source env and uploads them
|
|
87
|
+
to the target.
|
|
88
|
+
|
|
89
|
+
## Verify
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
plexus-cli --env staging --tenant <slug> files list # key, file, sha256, usedBy
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Open a draft application on the portal: the field shows a „Vorlage …" download
|
|
96
|
+
chip (or, for a document field, a „Vorlage" button inside the upload box). A
|
|
97
|
+
key the library no longer holds renders as **„Vorlage fehlt (<key>)"** — never
|
|
98
|
+
as a missing chip.
|
|
99
|
+
|
|
100
|
+
## Gotchas
|
|
101
|
+
|
|
102
|
+
- **Remove:** `plexus-cli files remove <key>` is refused while a form uses the
|
|
103
|
+
key; the error names `offer rN (status) / form / field` for each use. Detach
|
|
104
|
+
first (`offer form field detach --key … --file-key …`), republish, and note that
|
|
105
|
+
**archived** offer versions still count — applications pin them.
|
|
106
|
+
- Replacing or removing a file deletes its previous bytes in the same step (a
|
|
107
|
+
blank template has no audit value; the family's signed upload is the record).
|
|
108
|
+
Exception: a file the migration adopted from an old form attachment shares its
|
|
109
|
+
bytes with a document template (`ownsStorage: false`) — those go only once no
|
|
110
|
+
document template, resolver pin or template run of the tenant names them.
|
|
111
|
+
- The raw `storageId` attachment shape is retired; a stale bundle carrying one
|
|
112
|
+
fails publish with `[config.attachment-source]` naming the fix.
|
|
113
|
+
|
|
114
|
+
## Related
|
|
115
|
+
|
|
116
|
+
- UI: Daten → Dateien (upload, replace, delete).
|
|
117
|
+
- [Update a tenant's config](./update-tenant-config.md).
|