@cratis/pi 7.125.2 → 7.127.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/package/corpus/agents/spec-writer.md +2 -2
  2. package/package/corpus/harnesses/copilot/instructions/application-profile.instructions.md +1 -1
  3. package/package/corpus/harnesses/copilot/instructions/fast-feedback.instructions.md +37 -0
  4. package/package/corpus/harnesses/copilot/instructions/profiles.instructions.md +3 -2
  5. package/package/corpus/harnesses/copilot/instructions/secrets.instructions.md +1 -1
  6. package/package/corpus/harnesses/copilot/instructions/specs.csharp.instructions.md +1 -1
  7. package/package/corpus/harnesses/copilot/instructions/specs.instructions.md +7 -1
  8. package/package/corpus/harnesses/copilot/instructions/specs.scenarios.csharp.instructions.md +40 -3
  9. package/package/corpus/harnesses/copilot/instructions/vertical-slices.instructions.md +1 -1
  10. package/package/corpus/harnesses/pi/agents/spec-writer.md +2 -2
  11. package/package/corpus/hooks/README.md +1 -1
  12. package/package/corpus/hooks/scripts/cratis-guard-secrets.sh +13 -2
  13. package/package/corpus/rules/application-profile.md +1 -1
  14. package/package/corpus/rules/fast-feedback.md +36 -0
  15. package/package/corpus/rules/general.md +1 -0
  16. package/package/corpus/rules/profiles.md +3 -2
  17. package/package/corpus/rules/secrets.md +1 -1
  18. package/package/corpus/rules/specs.csharp.md +1 -1
  19. package/package/corpus/rules/specs.md +7 -1
  20. package/package/corpus/rules/specs.scenarios.csharp.md +40 -3
  21. package/package/corpus/rules/vertical-slices.md +1 -1
  22. package/package/corpus/skills/cratis-application-slice-specifications/SKILL.md +15 -10
  23. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/SKILL.md +1 -1
  24. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/authproxy.md +26 -0
  25. package/package/corpus/skills/cratis-arc-command/SKILL.md +7 -4
  26. package/package/corpus/skills/cratis-chronicle-event-specifications/SKILL.md +5 -0
  27. package/package/corpus/skills/cratis-chronicle-reactor/references/specifications.md +23 -2
  28. package/package/corpus/skills/cratis-chronicle-read-model-specifications/SKILL.md +6 -3
  29. package/package/corpus/skills/cratis-chronicle-reducer/SKILL.md +2 -2
  30. package/package/corpus/skills/cratis-code-review/SKILL.md +4 -2
  31. package/package/corpus/skills/cratis-environments-and-secrets/LICENSE +9 -0
  32. package/package/corpus/skills/cratis-environments-and-secrets/SKILL.md +112 -0
  33. package/package/corpus/skills/cratis-environments-and-secrets/references/environment-file.md +48 -0
  34. package/package/corpus/skills/cratis-onboarding-runtime/LICENSE +9 -0
  35. package/package/corpus/skills/cratis-onboarding-runtime/SKILL.md +94 -0
  36. package/package/corpus/skills/cratis-onboarding-runtime/references/ante-onboarding-keys.md +31 -0
  37. package/package/corpus/skills/cratis-onboarding-runtime/references/performance.md +86 -0
  38. package/package/corpus/skills/cratis-screenplay-authentication-and-onboarding/LICENSE +9 -0
  39. package/package/corpus/skills/cratis-screenplay-authentication-and-onboarding/SKILL.md +111 -0
  40. package/package/corpus/skills/cratis-screenplay-authentication-and-onboarding/references/onboarding-directives.md +89 -0
  41. package/package/corpus/skills/cratis-screenplay-authentication-and-onboarding/verification.json +32 -0
  42. package/package/corpus/skills/cratis-screenplay-command-surface/references/policy-persona-seed.md +1 -0
  43. package/package/corpus/skills/cratis-screenplay-model-authoring/references/language-reference.md +1 -1
  44. package/package/corpus/skills/cratis-screenplay-model-review/SKILL.md +2 -0
  45. package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/references/renderability-gate.md +8 -0
  46. package/package/corpus/skills/cratis-screenplay-toolchain/references/neighbouring-skills.md +2 -0
  47. package/package/corpus/skills/cratis-screenplay-toolchain/references/screenplay-contract-exceptions.json +70 -0
  48. package/package/corpus/skills/cratis-showrunner-herald/LICENSE +9 -0
  49. package/package/corpus/skills/cratis-showrunner-herald/SKILL.md +106 -0
  50. package/package/corpus/skills/cratis-showrunner-herald/references/options.md +35 -0
  51. package/package/corpus/skills/cratis-showrunner-herald/references/templates-and-images.md +70 -0
  52. package/package/corpus/skills/cratis-specifications-csharp/SKILL.md +2 -2
  53. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/SKILL.md +5 -0
  54. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/rendered-application.md +39 -0
  55. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/sandbox-and-specrunner.md +7 -0
  56. package/package/profile-catalog.json +6 -2
  57. package/package.json +1 -1
@@ -49,6 +49,32 @@ is a crawler policy, not authorization. Invitation error-page tokens
49
49
  letters, digits and `._+-@`; do not assume these placeholders always contain text.
50
50
  Signed-in `/register` forwarding needs no host workaround.
51
51
 
52
+ ## Starting AuthProxy from a Screenplay application's contract
53
+
54
+ Requires AuthProxy 2.40.0 or later for `Cratis:AuthProxy:Application`; an older image ignores it and starts with no providers.
55
+
56
+ AuthProxy can start from two inputs: the **provider list** of a Screenplay model (kind and identity per provider) and the **values of a Cratis environment**. It translates them onto its ordinary `Cratis:AuthProxy` options, so an application behind it trusts the proxy exactly as described above. The model never carries provider configuration; see `cratis-screenplay-authentication-and-onboarding` for the keys and `cratis-environments-and-secrets` for the values.
57
+
58
+ ```json
59
+ {
60
+ "Cratis": { "AuthProxy": { "Application": {
61
+ "EnvironmentName": "production",
62
+ "Providers": [
63
+ { "Kind": "EntraId", "Identity": "EntraId" },
64
+ { "Kind": "OpenId", "Identity": "Partner" }
65
+ ]
66
+ } } }
67
+ }
68
+ ```
69
+
70
+ - A provider's `Kind` is `EntraId`, `GitHub`, `Google`, `Apple` or `OpenId`; `Identity` is the model's alias, otherwise the provider name, unique ignoring case. The identity is also the scheme (lowercase, spaces to hyphens), so the callback is `/signin-<scheme>`.
71
+ - Required keys per kind: `clientId` and `clientSecret` for all; plus `tenantId` for `EntraId` and `authority` for `OpenId`. Each is addressed as `authentication.providers.<Identity>.<key>`.
72
+ - The contract cannot be combined with providers configured directly under `Authentication:OidcProviders` or `OAuthProviders`; AuthProxy refuses to start.
73
+ - A missing required key stops AuthProxy before it listens (exit code 78), naming the key, provider and environment, never a value.
74
+ - **Secrets are never read from arguments and never logged.** A key resolves from `Cratis:AuthProxy:Application:Environment:<key>`, then the same key with `_FILE` appended (a path to a file holding the value), then a file named exactly `<key>` in `SecretsDirectory`. Mount secrets as files and point `SecretsDirectory` at the folder; do not put a secret in an environment variable or an image.
75
+ - `Application:Development=true` allows HTTP only on loopback addresses; every other URL must be HTTPS. Never set it outside local development.
76
+ - With `Application:Onboarding=true`, supply the `onboarding.*` keys for the application's own Ante and every provider also gets invitation completion; see `cratis-onboarding-runtime`.
77
+
52
78
  ## Tenants and services
53
79
 
54
80
  AuthProxy resolves the tenant through ordered strategies under `Cratis:AuthProxy:TenantResolutions` (`Host`, `Claim`, `Route`, `Specified`, `Default`, `SubHost`, `Selection`). The result reaches the backend as `x-cratis-tenant-id`, which is Arc's default tenant header, so an Arc application behind AuthProxy needs no tenancy configuration and the `UseHeaderTenancy("Tenant-ID")` workaround seen in older applications can be removed.
@@ -274,10 +274,13 @@ Components guidance, not to this skill.
274
274
 
275
275
  ## Verify
276
276
 
277
- Start pure `Handle()` and validator decisions with direct calls and supplied
278
- read-model state. Add scenarios only for pipeline/authorization wiring,
279
- constraints, never read-protection plumbing or simulated concurrency;
280
- follow the [scenario selection and cost rule](../../rules/specs.scenarios.csharp.md).
277
+ Keep `Handle` a pure function of the command, its read-model parameters and
278
+ provided values: that makes it specifiable by a plain call. Start `Handle()` and
279
+ validator decisions with direct calls and supplied read-model state. Add the
280
+ pipeline scenario plus a negative proof per authorization policy, validator
281
+ rejection and `Provide()` short-circuit, and a constraint scenario; never
282
+ read-protection plumbing or simulated concurrency. Follow the
283
+ [decision table](../../rules/specs.scenarios.csharp.md).
281
284
 
282
285
  - The command is a `record`, carries `[Command]`, and declares a public instance
283
286
  `Handle`.
@@ -10,6 +10,11 @@ license: MIT
10
10
  database, no network — an empty event log at sequence number zero, with the
11
11
  application's constraints discovered and active.
12
12
 
13
+ Specify every constraint with an `EventScenario`: one class for the violation
14
+ and one for the nearest non-violation. A constraint is enforced at append, so no
15
+ plain call or substituted event store proves it. Pure decisions around it are
16
+ plain calls; see the [decision table](../../rules/specs.scenarios.csharp.md).
17
+
13
18
  ## Verified product sources
14
19
 
15
20
  | Package | Purpose |
@@ -12,8 +12,29 @@
12
12
 
13
13
  Start with `Specification` plus a direct call for pure reactor decisions,
14
14
  passing the trigger and supplied values explicitly and asserting the returned
15
- command or event. Add `ReactorScenario<TReactor>` only where invocation,
16
- dependency wiring or side effects contribute proof. In those scenarios, drive
15
+ command or event. Add one `ReactorScenario<TReactor>` per reactor for what only
16
+ Chronicle proves: dispatch, filtering, read-model parameters and routing of
17
+ produced events to the right event source (the
18
+ [decision table](../../../rules/specs.scenarios.csharp.md)).
19
+ Substitute the reactor's collaborators in either style; never substitute the
20
+ event store as proof of routing.
21
+
22
+ ```csharp
23
+ public class when_an_author_is_registered : Specification
24
+ {
25
+ readonly INotifier _notifier = Substitute.For<INotifier>();
26
+ readonly AuthorId _id = AuthorId.New();
27
+
28
+ async Task Because() =>
29
+ await new AuthorNotifications(_notifier).Registered(
30
+ new AuthorRegistered("Jane Austen"),
31
+ EventContext.EmptyWithEventSourceId(_id));
32
+
33
+ [Fact] void should_notify_about_the_author() => _notifier.Received(1).Notify("Jane Austen");
34
+ }
35
+ ```
36
+
37
+ In reactor scenarios, drive
17
38
  events with a service provider of substitutes and assert on the substitutes for
18
39
  non-event side effects; for handlers that return events, assert the resulting
19
40
  appends through the scenario's event store. Cover the replay path separately
@@ -16,9 +16,12 @@ default, with the event and current state supplied explicitly (for a self model,
16
16
  `ReadModelScenario<TReadModel>` only where declarative projection behavior,
17
17
  event dispatch, filtering, metadata or read-model wiring contribute proof.
18
18
  Follow the [scenario selection and cost rule](../../rules/specs.scenarios.csharp.md):
19
- use one scenario class per read model, many read-only assertions on one shared
20
- setup, and keep shared lifecycle enabled. Do not create a class per property,
21
- mapping attribute or lifecycle event. Measure scenario cost with TRX durations
19
+ use one scenario class per read model and per distinct event sequence the
20
+ projection declares (lifecycle, join, children with two keys, removal), with many
21
+ read-only assertions on one shared setup, and keep shared lifecycle enabled. Cover
22
+ each declared sequence; do not create a class per property or mapping attribute.
23
+ Never replace the engine with a substitute store as projection proof. Reducer
24
+ transitions are plain calls; the scenario proves dispatch and filters. Measure scenario cost with TRX durations
22
25
  using [running-specs.md](../cratis-specifications-csharp/references/running-specs.md).
23
26
 
24
27
  For a read-model scenario specification the **`Given` *is* the act**. There
@@ -271,8 +271,8 @@ Read [references/filtering.md](references/filtering.md) when restricting which e
271
271
 
272
272
  Start with `Specification` plus a direct call for pure reducer decisions:
273
273
  pass the event and current state explicitly and assert the returned next state.
274
- Add `ReadModelScenario<TReadModel>` only where event dispatch, filtering,
275
- metadata or read-model wiring contribute proof; drive the event sequence and
274
+ Add one `ReadModelScenario<TReadModel>` per reducer for event dispatch, filtering,
275
+ metadata and read-model wiring (the [decision table](../../rules/specs.scenarios.csharp.md)); drive the event sequence and
276
276
  assert on the resulting instance there. For the self example:
277
277
 
278
278
  Read [references/specification-example.md](references/specification-example.md) when you need a worked direct-call and `ReadModelScenario` spec.
@@ -155,8 +155,10 @@ degradations a reviewer can see without measuring:
155
155
  ## Step 6 — Specification coverage
156
156
 
157
157
  - Pure command, validator, reducer and reactor decisions have plain-call specs
158
- with supplied read-model state; scenarios add only pipeline/authorization
159
- wiring, constraints, declarative mapping or application-owned race-policy proof.
158
+ with supplied read-model state; scenarios cover exactly the obligations in the
159
+ per-artifact decision table of `specs.scenarios.csharp.md` (pipeline and
160
+ authorization, constraints, projection and reducer dispatch, reactor dispatch
161
+ and routing) plus application-owned race-policy proof.
160
162
  - Every State Change command has a happy-path specification.
161
163
  - Every validation rule has a direct-call failure spec. Pipeline validation
162
164
  specs assert **both** `ShouldNotBeSuccessful()` and `ShouldHaveValidationErrors()`.
@@ -0,0 +1,9 @@
1
+ Copyright (c) Cratis. All rights reserved.
2
+ Licensed under the MIT license. See LICENSE file in the canonical Cratis/AI repository for the full license text.
3
+
4
+ Third-party notices
5
+
6
+ This skill adapts material from agentic-engineer by Martin Dilger and Nebulit GmbH
7
+ (https://github.com/Nebulit-GmbH/agentic-engineer, commit 07b0f30648d663cb588d7e2c7aa031af9dfc21f2;
8
+ https://nebulit.de), used with the explicit consent of Martin Dilger and Nebulit GmbH. The adapted
9
+ material remains credited to Martin Dilger and Nebulit GmbH.
@@ -0,0 +1,112 @@
1
+ ---
2
+ name: cratis-environments-and-secrets
3
+ description: Use when a Cratis project needs named environments, per-environment configuration values or secrets with `cratis env` and `cratis config`, or when a Screenplay model's environment keys must be filled. Not for Chronicle server contexts or for authoring the model.
4
+ license: MIT
5
+ ---
6
+
7
+ # Cratis environments and secrets
8
+
9
+ A Screenplay model is the same in every environment. What it needs to run (a client id, an SMTP
10
+ host, a signing key) is not. A project keeps that in **named environments**, one committed file
11
+ per environment next to the model, in the way a Pulumi project has stacks. Which keys a model
12
+ needs is derived from its declarations: see
13
+ [cratis-screenplay-authentication-and-onboarding](../cratis-screenplay-authentication-and-onboarding/SKILL.md).
14
+
15
+ **Requires** `cratis` 3.114.0 or later for `cratis env` and `cratis config` (`cratis --version`,
16
+ and `which -a cratis` for a shadowing copy). The commands do not exist in older releases.
17
+
18
+ This is not a Chronicle server context: `cratis context` and `-e/--event-store` on `chronicle`
19
+ commands belong to `cratis-chronicle-cli-operations`.
20
+
21
+ ## The secrets contract
22
+
23
+ This skill applies the always-on [secrets rule](../../rules/secrets.md); read it first. For this
24
+ CLI it means:
25
+
26
+ - **A secret enters only through stdin or the hidden prompt.** Never as an argument (it lands in shell history and the transcript).
27
+ - **An agent never runs `cratis config get <key> --show-secret`** and never prints a secret by
28
+ any other route. Without the flag a secret prints as `[secret]`, which is the form to use.
29
+ - **An agent does not receive the value to set.** When a secret is needed and the person has not
30
+ provided it through a channel the agent cannot read, ask them to run the `--secret` command in
31
+ their own terminal. Do not ask them to paste it into the chat.
32
+ - **A passphrase is not a value to hold.** It comes from `CRATIS_PASSPHRASE`, the file named by
33
+ `CRATIS_PASSPHRASE_FILE`, or the hidden prompt. Never write one to a file in the repository,
34
+ and never export one inline in a command.
35
+
36
+ ## Files and selection
37
+
38
+ Each environment is `.cratis/environments/<name>.yaml`; a name is a safe file name such as
39
+ `development`, `staging` or `customer-x`. Entries are a plain `key: value`, a `secure: <base64>`
40
+ secret encrypted by the passphrase provider (safe to commit), or a `ref:` to a vault. The file
41
+ never holds a clear-text secret. Read [environment file](references/environment-file.md) for the
42
+ exact shape.
43
+
44
+ The environment in effect is, in order: `--environment <name>` (`-e` on `env` and `config`),
45
+ `CRATIS_ENVIRONMENT`, the user's selection in `.cratis/local/selected-environment` (not committed), then
46
+ `development`. On Chronicle commands `-e` means `--event-store`; use `--environment` there.
47
+
48
+ ## Commands
49
+
50
+ ```bash
51
+ cratis env init # creates "development"
52
+ cratis env init production --secrets-provider azurekeyvault://<vault>
53
+ cratis env select production
54
+ cratis env ls
55
+ cratis config set auth:clientId <client-id>
56
+ cratis config set smtp:password --secret # reads stdin or a hidden prompt
57
+ cratis config get smtp:password # [secret]
58
+ cratis config # list; secrets show as [secret]
59
+ cratis config --missing ./model # keys the model needs and the environment lacks
60
+ ```
61
+
62
+ | Command | Purpose |
63
+ |---|---|
64
+ | `cratis env init [name]` | Create an environment; `--secrets-provider` picks where secrets live. |
65
+ | `cratis env select <name>` / `ls` / `rm <name>` | Select for this user, list (the one in effect is marked), remove (confirms; `--yes` skips). |
66
+ | `cratis env change-secrets-provider <uri>` | Move every secret to another provider; the file never holds clear text in between. |
67
+ | `cratis env change-passphrase` | Re-encrypt every secret under a new passphrase and salt. |
68
+ | `cratis config set <key> <value>` | Set a plain value. |
69
+ | `cratis config set <key> --secret` | Set a secret. |
70
+ | `cratis config get <key>` / `rm <key>` | Read (secrets masked) / remove (also from the vault). |
71
+ | `cratis config [--missing <path>]` | List keys, or the keys a model needs that are absent. |
72
+
73
+ All accept `-o json`. Exit codes: `0` success, `1` not found, `4` authentication failed (wrong
74
+ passphrase), `5` validation, including any key missing under `--missing`.
75
+
76
+ ## Choosing a secrets provider
77
+
78
+ Default to **passphrase** for a project with one team and a committed environment file. Choose a
79
+ vault when secrets must not travel with the repository or are shared with a cluster.
80
+
81
+ | Provider | Use when | Credentials come from |
82
+ |---|---|---|
83
+ | `passphrase` (default) | Secrets are committed encrypted (AES-256-GCM, key derived per environment). | `CRATIS_PASSPHRASE`, `CRATIS_PASSPHRASE_FILE` or a prompt. |
84
+ | `azurekeyvault://<vault>` | The team already runs Azure Key Vault. | `DefaultAzureCredential` (Azure CLI login, managed or workload identity). |
85
+ | `hashivault://<address>/<mount>[/<path>]` | The team runs HashiCorp Vault KV v2. `hashivault+http://` is for a development server. | `VAULT_TOKEN`, or `VAULT_ROLE_ID` and `VAULT_SECRET_ID`, or `VAULT_K8S_ROLE`. |
86
+
87
+ `cratis run` on a model with `authentication` resolves the selected environment (or `-e <name>`)
88
+ before starting Docker, hands the provider and onboarding values to the AuthProxy and Ante
89
+ containers, and stops with exit `5` listing the missing keys, never their values. A mail
90
+ server is optional: without `onboarding.email.server.host`, emails are written as `.eml` files.
91
+ Requires cratis CLI 3.116.0 (`cratis --version`); older CLIs run the Stage alone and play a persona.
92
+
93
+ ## Pitfalls
94
+
95
+ - Setting a secret with `cratis config set smtp:password <value>` stores a plain value and leaks it. Always add `--secret` and pipe or prompt.
96
+ - Committing a `.cratis/local/` folder: it holds the personal selection and is git-ignored by `cratis init` and `cratis env select`.
97
+ - Wrong passphrase is exit `4` before anything is decrypted; do not retry it in a loop.
98
+ - `cratis config --missing` on a model that does not compile exits `5` and names the first errors; fix the model first.
99
+ - A key added to the model later is missing from existing environments; re-run `--missing` per environment.
100
+
101
+ ## Verify
102
+
103
+ 1. `cratis env ls` marks the environment you intend; `cratis config -e <name>` lists the expected keys.
104
+ 2. `cratis config --missing <model-folder> -e <name>` exits `0` (`echo $?`).
105
+ 3. `git diff` on `.cratis/environments/` shows only plain values, `secure:` or `ref:` entries, and no clear-text secret.
106
+ 4. No command in the session used `--show-secret`, and no secret appears in the transcript.
107
+
108
+ ## Route near misses
109
+
110
+ - Chronicle server contexts and operations: `cratis-chronicle-cli-operations`.
111
+ - The model's `authentication` and `onboarding` blocks and their keys: `cratis-screenplay-authentication-and-onboarding`.
112
+ - Running the application with the resolved values: `cratis-onboarding-runtime` (cluster) and `cratis-stage-rendering-and-sandbox`.
@@ -0,0 +1,48 @@
1
+ # Environment file
2
+
3
+ ```yaml
4
+ secretsProvider: passphrase
5
+ encryptionSalt: <base64>
6
+ encryption:
7
+ kdf: pbkdf2-sha256
8
+ iterations: 600000
9
+ check: <base64>
10
+ config:
11
+ auth:clientId: <client-id>
12
+ smtp:host: mail.example.com
13
+ smtp:password:
14
+ secure: <base64>
15
+ signing:key:
16
+ ref: <vault reference>
17
+ ```
18
+
19
+ | Entry | Meaning |
20
+ |---|---|
21
+ | `key: value` | A plain value. |
22
+ | `key: { secure: <base64> }` | A secret encrypted by the passphrase provider; bound to its key, so moving the ciphertext to another key fails. Safe to commit. |
23
+ | `key: { ref: <name> }` | A secret kept in Azure Key Vault or HashiCorp Vault. The file holds only the reference. |
24
+
25
+ `secretsProvider`, `encryptionSalt` and `encryption` describe where secrets live. The salt is per
26
+ environment, so two environments with one passphrase get different ciphertext. Do not edit these
27
+ fields by hand; use `cratis env change-passphrase` or `change-secrets-provider`.
28
+
29
+ ## Vault storage
30
+
31
+ - Azure Key Vault: secrets named `cratis-<environment>-<key>` with a short hash suffix; the file holds the name as `ref:`.
32
+ - HashiCorp Vault: KV v2 at `<path>/<environment>/<key>` (the path defaults to `cratis`); the file holds that path as `ref:`.
33
+ - `VAULT_AUTH_MOUNT` overrides the auth method mount and `VAULT_NAMESPACE` sets the Vault namespace.
34
+
35
+ ## Reading environments from code
36
+
37
+ The `Cratis.Environments` package reads the same files, so other tools resolve values and
38
+ secrets exactly as the CLI does.
39
+
40
+ ```csharp
41
+ var environments = ProjectEnvironments.Discover(Directory.GetCurrentDirectory());
42
+ var selected = environments.Resolve(explicitName: null);
43
+ var environment = ProjectEnvironment.Open(environments, selected.Name, PassphraseSources.FromEnvironment());
44
+
45
+ var clientId = await environment.GetValueAsync("auth:clientId");
46
+ ```
47
+
48
+ Keep a resolved secret in memory only; never log it or write it to a file the repository tracks.
@@ -0,0 +1,9 @@
1
+ Copyright (c) Cratis. All rights reserved.
2
+ Licensed under the MIT license. See LICENSE file in the canonical Cratis/AI repository for the full license text.
3
+
4
+ Third-party notices
5
+
6
+ This skill adapts material from agentic-engineer by Martin Dilger and Nebulit GmbH
7
+ (https://github.com/Nebulit-GmbH/agentic-engineer, commit 07b0f30648d663cb588d7e2c7aa031af9dfc21f2;
8
+ https://nebulit.de), used with the explicit consent of Martin Dilger and Nebulit GmbH. The adapted
9
+ material remains credited to Martin Dilger and Nebulit GmbH.
@@ -0,0 +1,94 @@
1
+ ---
2
+ name: cratis-onboarding-runtime
3
+ description: Use when running or deploying the onboarding of a Screenplay application, meaning configuring the application's Ante from the onboarding contract or declaring a Showrunner Stage Manager `Performance` that runs the application with its AuthProxy and Ante. Not for authoring the model or for Ante used by a host product.
4
+ license: MIT
5
+ ---
6
+
7
+ # Onboarding runtime: Ante and Stage Manager
8
+
9
+ A Screenplay model with `authentication` and `onboarding` is carried out by three runtime parts
10
+ that read one contract, produced from the model and a Cratis environment:
11
+
12
+ | Part | Does | Contract |
13
+ |---|---|---|
14
+ | **AuthProxy** | The only public entry point; signs people in with the model's providers. | `Cratis:AuthProxy:Application` (see the AuthProxy reference of `cratis-arc-authentication-authorization-and-identity`). |
15
+ | **Ante** | **One per application.** Signs invitation tokens, serves the lobby, records acceptance and legal terms, publishes to its outbox. | `Ante:Onboarding:*`. |
16
+ | **Herald** | Sends the invitation and welcome emails from the application (the declined template waits for an Ante outcome). | `cratis-showrunner-herald`. |
17
+
18
+ Nobody writes the equivalent `Ante:*` or `Cratis:AuthProxy:*` keys by hand for a Screenplay
19
+ application. The model supplies structure (providers, personas, invitation types, legal files);
20
+ the **environment** supplies every value and secret (`cratis-environments-and-secrets`).
21
+
22
+ **Requires** Ante 1.5.0 or later for `Ante:Onboarding:*`, AuthProxy 2.40.0 or later for
23
+ `Cratis:AuthProxy:Application`, and Showrunner 1.11.0 or later for the `Performance` resource.
24
+
25
+ ## Who owns what
26
+
27
+ Ante owns token signing, the lobby, local acceptance, legal terms presented from files, and the
28
+ persona and invitation-type checks. The **application** owns the persona list (the model), who is
29
+ invited, link delivery, membership and provisioning. Ante sends no email: the application sends
30
+ the invitation link through Herald using the `InvitationTokenIssued` token. Every accepted fact
31
+ (`InvitationToJoinTenantAccepted`, `InvitationToCreateTenantAccepted`,
32
+ `OrganizationRegistrationCompleted`, `LegalTermsAccepted`) means **published to Ante's outbox, not
33
+ provisioned**: make provisioning idempotent by event source id and event kind. Ante does not know
34
+ what a persona may do; it validates grantable personas against the configured list only.
35
+
36
+ ## Ante's onboarding contract
37
+
38
+ Set `Ante:Onboarding:Enabled=true` to apply it. While `false` or absent every `Ante:Onboarding:*`
39
+ key is ignored. When enabled, each value it carries wins over the ordinary key it maps to,
40
+ including values from `appsettings.json`. Read [Ante onboarding keys](references/ante-onboarding-keys.md)
41
+ for the table before configuring anything.
42
+
43
+ - `HostStore` is the application's own Chronicle store, the only host store Ante observes. The application appends `UserInvitedToJoinTenant` or `UserInvitedToCreateTenant` to that store's outbox.
44
+ - `SignUp` is `CreateTenant` (default; self-service registration may create a tenant) or `JoinTenant` (`/register` closed; invitation only). This is the model's `signup` line.
45
+ - `InvitationTypes` and `Personas` are runtime checks: an invitation of a disallowed type is rejected with `InvitationTypeNotAllowed`, one granting a persona outside the list with `PersonaNotGrantable`, and no token is issued either way. Validate against the model's persona list in the application first; Ante is the second line of defense. Observe generation 4 of `InvitationRejected`.
46
+ - Legal terms are files in a mounted folder (`terms.md`, `privacy.md`). The version is a content hash, so editing either file requires everyone onboarding afterwards to accept again. Archive each version for consent audit; the bodies live in your files.
47
+ - **Secrets** (the signing private key, pinned attestation public keys) arrive only as mounted files or standard configuration, never on the command line; Ante logs no value.
48
+
49
+ ## Development mode is for local runs only
50
+
51
+ `Ante:Onboarding:Development:Enabled=true` generates a 2048-bit RSA signing key per data volume and
52
+ needs no secret. Ante refuses to start when the environment name is production-like (`Production`,
53
+ `Prod`, `Live`, `Staging`, `Stage`, `Preprod`), in a Kubernetes pod, or when a configured URL is not
54
+ loopback. Never copy a development key to another environment, and leave `Secrets:SigningKeyFile` out of a development run.
55
+
56
+ Locally, `cratis run` on a model with `onboarding` starts Ante this way: `ASPNETCORE_ENVIRONMENT`
57
+ is `Development` and `Ante:Onboarding:Development:Enabled` is `true`, so Ante generates its signing
58
+ key into a folder that lives only for the run. It shares AuthProxy's network stack, so AuthProxy
59
+ reaches it on loopback, and the lobby is served through AuthProxy. Nothing from a local run is
60
+ trusted by another run or cluster.
61
+
62
+ ## Stage Manager `Performance`
63
+
64
+ A `Performance` (`showrunner.cratis.io/v1alpha1`) runs one application in one Cratis environment,
65
+ with its AuthProxy when the application has `authentication` and its own Ante when it has
66
+ `onboarding`. Read [Performance resource](references/performance.md) to write or review the spec.
67
+
68
+ - The environment file is supplied as a ConfigMap key (the file `cratis` keeps in `.cratis/environments/<name>.yaml`); a passphrase environment names a Secret for the passphrase. Vault access belongs to the operator's identity, set in the chart, not the Performance.
69
+ - **A resolved value reaches a container only as a file in a mounted Secret volume**: never an environment variable, a ConfigMap, the status or the operator log.
70
+ - Nothing is created until everything is in hand. A missing environment key makes the phase `Blocked` and lists the keys in `status.missingEnvironmentKeys`; running Deployments are left alone.
71
+ - AuthProxy is the only ingress target; the application and Ante are ClusterIP Services behind it.
72
+ - The operator generates and rotates the invitation and attestation signing keys (`spec.signingKeys.rotation`) and derives the issuers, audiences and URLs, so none of them is written twice.
73
+
74
+ ## Pitfalls
75
+
76
+ - Treating `Accepted` as provisioned: provisioning is the application's, and must be idempotent.
77
+ - Setting a signing key or provider secret as an environment variable in a manifest: use the mounted files.
78
+ - Two Ante instances for one application, or one Ante shared by several Performances: one Ante per Performance, with its own event store (`ante-<name>` by default).
79
+ - Using development mode in a shared cluster: Ante refuses; do not work around the guard.
80
+
81
+ ## Verify
82
+
83
+ 1. The model compiles and `screenplay environment <folder>` lists the keys; `cratis config --missing <folder> -e <name>` exits `0`.
84
+ 2. `kubectl get performance <name> -o jsonpath='{.status.phase}'` is `Ready`; `-o jsonpath='{.status.missingEnvironmentKeys}'` is empty.
85
+ 3. An invitation for a persona outside the model is rejected with `PersonaNotGrantable` and issues no token.
86
+ 4. No secret value appears in a manifest, the status, a log or the transcript (`kubectl get -o json` on pods or secrets is forbidden by the secrets rule; project fields instead).
87
+
88
+ ## Route near misses
89
+
90
+ - The model's `onboarding` block and environment keys: `cratis-screenplay-authentication-and-onboarding`.
91
+ - Environment commands and secrets providers: `cratis-environments-and-secrets`.
92
+ - Sending the emails: `cratis-showrunner-herald`.
93
+ - Ante used by a host product, not a Screenplay application: Ante's own documentation; this corpus does not cover it.
94
+ - Chronicle itself in a cluster: `cratis-chronicle-hosting`.
@@ -0,0 +1,31 @@
1
+ # Ante onboarding keys
2
+
3
+ Configuration keys under `Ante:Onboarding:` when `Enabled` is `true`. Each value maps onto the
4
+ ordinary key shown and wins over it. In environment-variable form use `__` for `:`.
5
+
6
+ | Key | Type | Maps to | Notes |
7
+ |---|---|---|---|
8
+ | `Enabled` | bool | none | Applies the contract. Default `false`. |
9
+ | `HostStore` | string | `Ante:HostStores` (one entry) | The application's own Chronicle store. Required unless development mode (then `Application`). |
10
+ | `EventStore`, `Namespace` | string | `Ante:EventStore`, `Ante:Namespace` | Optional. Ante's own store and fixed namespace. |
11
+ | `PublicBaseUrl` | absolute URL | `Ante:Invitations:Token:PublicBaseUrl` | Where users reach this Ante. HTTPS, or HTTP on loopback. |
12
+ | `ApplicationUrl` | URL | `Ante:HostAppUrl` | Where people go when onboarding completes. |
13
+ | `SignUp` | `CreateTenant` or `JoinTenant` | `Ante:Registration:Enabled` | `JoinTenant` closes `/register`. |
14
+ | `RegistrationEnabled` | bool, default `true` | `Ante:Registration:Enabled` | Always off when `SignUp` is `JoinTenant`. |
15
+ | `InvitationTypes` | list | none (runtime check) | `JoinTenant`, `CreateTenant`; unset allows both. |
16
+ | `Personas` | list | none (runtime check) | Grantable personas; ordinal comparison; unset or empty accepts any role. |
17
+ | `DefaultLocale`, `AllowedLocales` | string, list | `Ante:DefaultLocale`, `Ante:SupportedLocales` | |
18
+ | `Legal:Folder`, `Legal:TermsAndConditions`, `Legal:PrivacyPolicy` | paths | `Ante:Legal:Source=Files`, `Ante:Legal:Files:*` | Omit `Legal` for no legal step. |
19
+ | `Secrets:Path` | folder | any key | One file per setting, named like the key with `__` for `:`; trailing newline removed; names starting `.` skipped. |
20
+ | `Secrets:SigningKeyFile` | file path | `Ante:Invitations:Token:PrivateKeyPem` | PEM RSA private key, at least 2048 bits. |
21
+ | `Secrets:VerificationKeyFile` | file path | `Ante:Invitations:Token:PublicKeyPem` | Optional extra trusted public key. |
22
+ | `Development:Enabled`, `Development:DataPath` | bool, folder | none | Local runs only; see `SKILL.md`. |
23
+
24
+ The same instance still needs `Cratis:Chronicle:ConnectionString` and `Cratis:MongoDB`, normally
25
+ supplied by the Cratis environment. Attested-exchange settings, `Issuer`, `Audience` and `Expiry`
26
+ stay under `Ante:Invitations`.
27
+
28
+ Legal terms from files: the version is `sha256-` plus the first 16 hex characters of a SHA-256
29
+ over both bodies. The set is read on every request, so a changed mounted file applies without a
30
+ restart. Acceptance is recorded and published as `LegalTermsAccepted` carrying that version; Ante
31
+ does not publish `LegalDocumentSetActivated` for a file set.
@@ -0,0 +1,86 @@
1
+ # Performance resource
2
+
3
+ ```yaml
4
+ apiVersion: showrunner.cratis.io/v1alpha1
5
+ kind: Performance
6
+ metadata:
7
+ name: crm
8
+ namespace: apps
9
+ spec:
10
+ productionRef:
11
+ name: ledger
12
+ chronicle:
13
+ connectionSecretRef:
14
+ name: chronicle-connection
15
+ application:
16
+ image: registry.example.com/crm:1.0.0
17
+ eventStores: [Crm]
18
+ environment:
19
+ name: production
20
+ configMapRef:
21
+ name: crm-environment
22
+ passphraseSecretRef:
23
+ name: crm-passphrase
24
+ authProxy:
25
+ enabled: true
26
+ providers:
27
+ - kind: EntraId
28
+ identity: EntraId
29
+ onboarding:
30
+ enabled: true
31
+ signUp: CreateTenant
32
+ personas: [Administrator, Member]
33
+ legal:
34
+ configMapRef:
35
+ name: crm-legal
36
+ ingress:
37
+ enabled: true
38
+ className: nginx
39
+ hosts: [crm.example.com]
40
+ tlsIssuer:
41
+ name: letsencrypt
42
+ ```
43
+
44
+ ## Fields that decide behavior
45
+
46
+ - Chronicle: `chronicle.connectionSecretRef` (an existing Secret) takes precedence over `productionRef`. The Secret is mounted as a file, never copied. `application.eventStores[0]` is the store Ante observes.
47
+ - `environment.configMapRef` holds `<name>.yaml` unless `key` says otherwise. `passphraseSecretRef` is only for a passphrase environment. Azure Key Vault uses the operator's workload identity; HashiCorp Vault uses Kubernetes auth with the operator's service account and `VAULT_K8S_ROLE`; both are chart values.
48
+ - `authProxy.providers` carry kind and identity only, matching the model. `onboarding` carries `signUp`, `personas` and legal files; every value comes from the environment.
49
+ - `onboarding.attestedProviders` set: Ante runs attested invitation exchange against AuthProxy's attestations; unset keeps the legacy exchange.
50
+
51
+ ## Created resources (for a Performance named `crm`)
52
+
53
+ | Resource | Name | Notes |
54
+ |---|---|---|
55
+ | Deployment and Service | `crm-app`, `crm-authproxy` (when enabled), `crm-ante` (when onboarding) | ClusterIP; Ante has one replica. |
56
+ | Ingress | `crm` | Every host routes to AuthProxy only; certificate from cert-manager. |
57
+ | Secrets | `crm-app-environment`, `crm-authproxy-environment`, `crm-ante-secrets`, `crm-signing-keys` | Mounted content and generated keys. |
58
+
59
+ AuthProxy gets one file per environment key it needs in `/run/secrets/cratis`; Ante gets one file
60
+ per setting (`__` for `:`) in `/run/secrets/ante`; the application gets the remaining keys in
61
+ `/run/secrets/cratis`. Pods roll when mounted content changes, via a keyed hash annotation that
62
+ reveals nothing about a value. Ante's own MongoDB server comes from the environment key `ante.mongodb.server`.
63
+
64
+ ## Status
65
+
66
+ | Field | Meaning |
67
+ |---|---|
68
+ | `phase` | `Pending`, `Blocked`, `Progressing` or `Ready`. |
69
+ | `publicUrl` | `https://` and the first ingress host. |
70
+ | `missingEnvironmentKeys` | Keys the environment lacks while `Blocked`. |
71
+ | `conditions` | `SpecValid`, `ChronicleConnection`, `EnvironmentResolved`, `EnvironmentComplete`, and per component `ApplicationReady`, `AuthProxyReady`, `AnteReady`. |
72
+ | `signingKeys` | Key ids in use, rotation counter applied, previous public keys pinned. |
73
+
74
+ Conditions and status never contain a secret value. An unresolved environment is reported in
75
+ `EnvironmentResolved` with a message naming the key and the provider.
76
+
77
+ ## Signing keys
78
+
79
+ The operator generates the key Ante signs invitations with and the key AuthProxy signs
80
+ attestations with, in `crm-signing-keys`. Raise `spec.signingKeys.rotation` to rotate both. The
81
+ public half of each replaced key stays pinned (`signingKeys.retainPrevious`, two by default), so
82
+ Ante verifies invitations signed with a previous key.
83
+
84
+ Routing `/_invite` and `/.well-known/ante` from AuthProxy to Ante is configured on AuthProxy's
85
+ side; set `onboarding.lobbyBackendBaseUrl` in the environment when needed. Other Stage Manager
86
+ resources (`Production`, `Understudy`, `Restore`, `Rehearsal`, `BackstagePass`, `ControlRoom`) are schema only; nothing acts on them.
@@ -0,0 +1,9 @@
1
+ Copyright (c) Cratis. All rights reserved.
2
+ Licensed under the MIT license. See LICENSE file in the canonical Cratis/AI repository for the full license text.
3
+
4
+ Third-party notices
5
+
6
+ This skill adapts material from agentic-engineer by Martin Dilger and Nebulit GmbH
7
+ (https://github.com/Nebulit-GmbH/agentic-engineer, commit 07b0f30648d663cb588d7e2c7aa031af9dfc21f2;
8
+ https://nebulit.de), used with the explicit consent of Martin Dilger and Nebulit GmbH. The adapted
9
+ material remains credited to Martin Dilger and Nebulit GmbH.