@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.
- package/package/corpus/agents/spec-writer.md +2 -2
- package/package/corpus/harnesses/copilot/instructions/application-profile.instructions.md +1 -1
- package/package/corpus/harnesses/copilot/instructions/fast-feedback.instructions.md +37 -0
- package/package/corpus/harnesses/copilot/instructions/profiles.instructions.md +3 -2
- package/package/corpus/harnesses/copilot/instructions/secrets.instructions.md +1 -1
- package/package/corpus/harnesses/copilot/instructions/specs.csharp.instructions.md +1 -1
- package/package/corpus/harnesses/copilot/instructions/specs.instructions.md +7 -1
- package/package/corpus/harnesses/copilot/instructions/specs.scenarios.csharp.instructions.md +40 -3
- package/package/corpus/harnesses/copilot/instructions/vertical-slices.instructions.md +1 -1
- package/package/corpus/harnesses/pi/agents/spec-writer.md +2 -2
- package/package/corpus/hooks/README.md +1 -1
- package/package/corpus/hooks/scripts/cratis-guard-secrets.sh +13 -2
- package/package/corpus/rules/application-profile.md +1 -1
- package/package/corpus/rules/fast-feedback.md +36 -0
- package/package/corpus/rules/general.md +1 -0
- package/package/corpus/rules/profiles.md +3 -2
- package/package/corpus/rules/secrets.md +1 -1
- package/package/corpus/rules/specs.csharp.md +1 -1
- package/package/corpus/rules/specs.md +7 -1
- package/package/corpus/rules/specs.scenarios.csharp.md +40 -3
- package/package/corpus/rules/vertical-slices.md +1 -1
- package/package/corpus/skills/cratis-application-slice-specifications/SKILL.md +15 -10
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/SKILL.md +1 -1
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/authproxy.md +26 -0
- package/package/corpus/skills/cratis-arc-command/SKILL.md +7 -4
- package/package/corpus/skills/cratis-chronicle-event-specifications/SKILL.md +5 -0
- package/package/corpus/skills/cratis-chronicle-reactor/references/specifications.md +23 -2
- package/package/corpus/skills/cratis-chronicle-read-model-specifications/SKILL.md +6 -3
- package/package/corpus/skills/cratis-chronicle-reducer/SKILL.md +2 -2
- package/package/corpus/skills/cratis-code-review/SKILL.md +4 -2
- package/package/corpus/skills/cratis-environments-and-secrets/LICENSE +9 -0
- package/package/corpus/skills/cratis-environments-and-secrets/SKILL.md +112 -0
- package/package/corpus/skills/cratis-environments-and-secrets/references/environment-file.md +48 -0
- package/package/corpus/skills/cratis-onboarding-runtime/LICENSE +9 -0
- package/package/corpus/skills/cratis-onboarding-runtime/SKILL.md +94 -0
- package/package/corpus/skills/cratis-onboarding-runtime/references/ante-onboarding-keys.md +31 -0
- package/package/corpus/skills/cratis-onboarding-runtime/references/performance.md +86 -0
- package/package/corpus/skills/cratis-screenplay-authentication-and-onboarding/LICENSE +9 -0
- package/package/corpus/skills/cratis-screenplay-authentication-and-onboarding/SKILL.md +111 -0
- package/package/corpus/skills/cratis-screenplay-authentication-and-onboarding/references/onboarding-directives.md +89 -0
- package/package/corpus/skills/cratis-screenplay-authentication-and-onboarding/verification.json +32 -0
- package/package/corpus/skills/cratis-screenplay-command-surface/references/policy-persona-seed.md +1 -0
- package/package/corpus/skills/cratis-screenplay-model-authoring/references/language-reference.md +1 -1
- package/package/corpus/skills/cratis-screenplay-model-review/SKILL.md +2 -0
- package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/references/renderability-gate.md +8 -0
- package/package/corpus/skills/cratis-screenplay-toolchain/references/neighbouring-skills.md +2 -0
- package/package/corpus/skills/cratis-screenplay-toolchain/references/screenplay-contract-exceptions.json +70 -0
- package/package/corpus/skills/cratis-showrunner-herald/LICENSE +9 -0
- package/package/corpus/skills/cratis-showrunner-herald/SKILL.md +106 -0
- package/package/corpus/skills/cratis-showrunner-herald/references/options.md +35 -0
- package/package/corpus/skills/cratis-showrunner-herald/references/templates-and-images.md +70 -0
- package/package/corpus/skills/cratis-specifications-csharp/SKILL.md +2 -2
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/SKILL.md +5 -0
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/rendered-application.md +39 -0
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/sandbox-and-specrunner.md +7 -0
- package/package/profile-catalog.json +6 -2
- 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
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
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>`
|
|
16
|
-
|
|
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
|
|
20
|
-
|
|
21
|
-
|
|
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>`
|
|
275
|
-
metadata
|
|
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
|
|
159
|
-
|
|
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.
|