@cratis/pi 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/README.md +43 -0
  2. package/package.json +24 -0
  3. package/skills/cratis-application-react-specifications/LICENSE +2 -0
  4. package/skills/cratis-application-react-specifications/SKILL.md +192 -0
  5. package/skills/cratis-application-slice-specifications/LICENSE +2 -0
  6. package/skills/cratis-application-slice-specifications/SKILL.md +222 -0
  7. package/skills/cratis-arc-authentication-authorization-and-identity/LICENSE +2 -0
  8. package/skills/cratis-arc-authentication-authorization-and-identity/SKILL.md +233 -0
  9. package/skills/cratis-arc-authentication-authorization-and-identity/references/authentication.md +140 -0
  10. package/skills/cratis-arc-authentication-authorization-and-identity/references/authorization.md +112 -0
  11. package/skills/cratis-arc-authentication-authorization-and-identity/references/frontend.md +102 -0
  12. package/skills/cratis-arc-authentication-authorization-and-identity/references/local-development.md +85 -0
  13. package/skills/cratis-arc-authentication-authorization-and-identity/references/tenancy.md +87 -0
  14. package/skills/cratis-arc-command/LICENSE +2 -0
  15. package/skills/cratis-arc-command/SKILL.md +286 -0
  16. package/skills/cratis-arc-command/references/command-result.md +111 -0
  17. package/skills/cratis-arc-command/references/handler-shapes.md +135 -0
  18. package/skills/cratis-arc-command/references/proxy-generation.md +87 -0
  19. package/skills/cratis-arc-command/references/read-model-injection.md +97 -0
  20. package/skills/cratis-arc-command-execution/LICENSE +2 -0
  21. package/skills/cratis-arc-command-execution/SKILL.md +148 -0
  22. package/skills/cratis-arc-command-validation/LICENSE +2 -0
  23. package/skills/cratis-arc-command-validation/SKILL.md +230 -0
  24. package/skills/cratis-arc-ef-core-migration/LICENSE +2 -0
  25. package/skills/cratis-arc-ef-core-migration/SKILL.md +231 -0
  26. package/skills/cratis-arc-observable-query-http/LICENSE +2 -0
  27. package/skills/cratis-arc-observable-query-http/SKILL.md +157 -0
  28. package/skills/cratis-arc-query-paging/LICENSE +2 -0
  29. package/skills/cratis-arc-query-paging/SKILL.md +173 -0
  30. package/skills/cratis-arc-react-page/LICENSE +2 -0
  31. package/skills/cratis-arc-react-page/SKILL.md +291 -0
  32. package/skills/cratis-arc-react-page/references/data-page.md +125 -0
  33. package/skills/cratis-arc-react-page/references/data-tables.md +136 -0
  34. package/skills/cratis-arc-react-page/references/dialogs.md +238 -0
  35. package/skills/cratis-arc-react-page/references/mvvm.md +150 -0
  36. package/skills/cratis-arc-react-page/references/queries-and-commands.md +200 -0
  37. package/skills/cratis-chronicle-cli-operations/LICENSE +2 -0
  38. package/skills/cratis-chronicle-cli-operations/SKILL.md +199 -0
  39. package/skills/cratis-chronicle-client-dotnet/LICENSE +2 -0
  40. package/skills/cratis-chronicle-client-dotnet/SKILL.md +433 -0
  41. package/skills/cratis-chronicle-client-elixir/LICENSE +2 -0
  42. package/skills/cratis-chronicle-client-elixir/SKILL.md +429 -0
  43. package/skills/cratis-chronicle-client-kotlin/LICENSE +2 -0
  44. package/skills/cratis-chronicle-client-kotlin/SKILL.md +498 -0
  45. package/skills/cratis-chronicle-client-typescript/LICENSE +2 -0
  46. package/skills/cratis-chronicle-client-typescript/SKILL.md +396 -0
  47. package/skills/cratis-chronicle-compliance/LICENSE +2 -0
  48. package/skills/cratis-chronicle-compliance/SKILL.md +246 -0
  49. package/skills/cratis-chronicle-event-constraints/LICENSE +2 -0
  50. package/skills/cratis-chronicle-event-constraints/SKILL.md +227 -0
  51. package/skills/cratis-chronicle-event-modeling/LICENSE +2 -0
  52. package/skills/cratis-chronicle-event-modeling/SKILL.md +157 -0
  53. package/skills/cratis-chronicle-event-specifications/LICENSE +2 -0
  54. package/skills/cratis-chronicle-event-specifications/SKILL.md +200 -0
  55. package/skills/cratis-chronicle-event-type-migration/LICENSE +2 -0
  56. package/skills/cratis-chronicle-event-type-migration/SKILL.md +235 -0
  57. package/skills/cratis-chronicle-mcp-inspection/LICENSE +2 -0
  58. package/skills/cratis-chronicle-mcp-inspection/SKILL.md +79 -0
  59. package/skills/cratis-chronicle-mcp-inspection/references/blocked-tools.md +12 -0
  60. package/skills/cratis-chronicle-mcp-inspection/references/observational-tools.md +8 -0
  61. package/skills/cratis-chronicle-multi-tenancy/LICENSE +2 -0
  62. package/skills/cratis-chronicle-multi-tenancy/SKILL.md +198 -0
  63. package/skills/cratis-chronicle-projection/LICENSE +2 -0
  64. package/skills/cratis-chronicle-projection/SKILL.md +162 -0
  65. package/skills/cratis-chronicle-projection/references/fluent-builder.md +126 -0
  66. package/skills/cratis-chronicle-projection/references/model-bound-attributes.md +118 -0
  67. package/skills/cratis-chronicle-reactor/LICENSE +2 -0
  68. package/skills/cratis-chronicle-reactor/SKILL.md +344 -0
  69. package/skills/cratis-chronicle-read-model/LICENSE +2 -0
  70. package/skills/cratis-chronicle-read-model/SKILL.md +193 -0
  71. package/skills/cratis-chronicle-read-model/references/queries.md +142 -0
  72. package/skills/cratis-chronicle-read-model-specifications/LICENSE +2 -0
  73. package/skills/cratis-chronicle-read-model-specifications/SKILL.md +176 -0
  74. package/skills/cratis-chronicle-reducer/LICENSE +2 -0
  75. package/skills/cratis-chronicle-reducer/SKILL.md +173 -0
  76. package/skills/cratis-chronicle-web-workbench/LICENSE +2 -0
  77. package/skills/cratis-chronicle-web-workbench/SKILL.md +177 -0
  78. package/skills/cratis-cli-terminal-workbench/LICENSE +2 -0
  79. package/skills/cratis-cli-terminal-workbench/SKILL.md +156 -0
  80. package/skills/cratis-code-review/LICENSE +2 -0
  81. package/skills/cratis-code-review/SKILL.md +177 -0
  82. package/skills/cratis-components-accessibility/LICENSE +2 -0
  83. package/skills/cratis-components-accessibility/SKILL.md +176 -0
  84. package/skills/cratis-components-schema-editor/LICENSE +2 -0
  85. package/skills/cratis-components-schema-editor/SKILL.md +238 -0
  86. package/skills/cratis-components-stepper-command-dialog/LICENSE +2 -0
  87. package/skills/cratis-components-stepper-command-dialog/SKILL.md +209 -0
  88. package/skills/cratis-components-styling/LICENSE +2 -0
  89. package/skills/cratis-components-styling/SKILL.md +221 -0
  90. package/skills/cratis-components-toolbar/LICENSE +2 -0
  91. package/skills/cratis-components-toolbar/SKILL.md +259 -0
  92. package/skills/cratis-documentation-writing/LICENSE +2 -0
  93. package/skills/cratis-documentation-writing/SKILL.md +121 -0
  94. package/skills/cratis-event-model-diagram/LICENSE +2 -0
  95. package/skills/cratis-event-model-diagram/SKILL.md +142 -0
  96. package/skills/cratis-fundamentals-concept/LICENSE +2 -0
  97. package/skills/cratis-fundamentals-concept/SKILL.md +198 -0
  98. package/skills/cratis-fundamentals-type-discovery/LICENSE +2 -0
  99. package/skills/cratis-fundamentals-type-discovery/SKILL.md +152 -0
  100. package/skills/cratis-governed-release-methodology/LICENSE +2 -0
  101. package/skills/cratis-governed-release-methodology/SKILL.md +297 -0
  102. package/skills/cratis-lens-browser-extension/LICENSE +2 -0
  103. package/skills/cratis-lens-browser-extension/SKILL.md +239 -0
  104. package/skills/cratis-performance-review/LICENSE +2 -0
  105. package/skills/cratis-performance-review/SKILL.md +139 -0
  106. package/skills/cratis-screenplay-model-authoring/LICENSE +2 -0
  107. package/skills/cratis-screenplay-model-authoring/SKILL.md +231 -0
  108. package/skills/cratis-security-review/LICENSE +2 -0
  109. package/skills/cratis-security-review/SKILL.md +141 -0
  110. package/skills/cratis-specification-by-example/LICENSE +2 -0
  111. package/skills/cratis-specification-by-example/SKILL.md +188 -0
  112. package/skills/cratis-specifications-csharp/LICENSE +2 -0
  113. package/skills/cratis-specifications-csharp/SKILL.md +230 -0
  114. package/skills/cratis-specifications-csharp/references/application-scenarios.md +191 -0
  115. package/skills/cratis-specifications-csharp/references/csharp-patterns.md +142 -0
  116. package/skills/cratis-specifications-csharp/references/integration-specs.md +126 -0
  117. package/skills/cratis-specifications-typescript/LICENSE +2 -0
  118. package/skills/cratis-specifications-typescript/SKILL.md +202 -0
  119. package/skills/cratis-specifications-typescript/references/typescript-patterns.md +168 -0
  120. package/skills/cratis-stage-rendering-and-sandbox/LICENSE +2 -0
  121. package/skills/cratis-stage-rendering-and-sandbox/SKILL.md +230 -0
  122. package/skills/cratis-studio-mcp-safety-guidance/LICENSE +2 -0
  123. package/skills/cratis-studio-mcp-safety-guidance/SKILL.md +69 -0
  124. package/skills/cratis-studio-mcp-safety-guidance/references/blocked-tools.md +12 -0
  125. package/skills/cratis-studio-mcp-safety-guidance/references/observational-tools.md +8 -0
@@ -0,0 +1,199 @@
1
+ ---
2
+ name: cratis-chronicle-cli-operations
3
+ description: Inspect and operate a running Chronicle server with the cratis CLI - contexts and connection resolution, machine-readable output, failed partitions, observers, event sequences, read models, projections and jobs, and the authorization discipline for replay, retry, quarantine clearing and other mutations. Use when the question is about the state of a live store rather than source code. Do not use to diagnose a defect that reproduces locally in source.
4
+ license: MIT
5
+ ---
6
+
7
+ # Operate a running Chronicle with the `cratis` CLI
8
+
9
+ Source code says what *should* happen. When the question is what *is* happening
10
+ in a live store — a projection that will not move, an observer that stopped, an
11
+ event you are not sure was appended — read the server instead of the code.
12
+
13
+ ## Verified product sources
14
+
15
+ This skill is verified against these exact sources:
16
+
17
+ | Package | Version | Purpose |
18
+ | --- | --- | --- |
19
+ | `Cratis.Cli` | `2.4.0` | the `cratis` command, its contexts, output formats, and command tree |
20
+ | `Cratis.Chronicle` | `16.45.2` | the observer, partition, sequence, and job concepts the CLI reads |
21
+
22
+ The CLI ships a **versioned machine-readable catalog of its own surface**, and
23
+ that catalog is the authority for exact command names on the installed version:
24
+
25
+ ```bash
26
+ cratis llm-context # every command, option and argument as JSON
27
+ cratis llm-context --schema # the JSON Schema for that document
28
+ cratis <group> --help # the same, one group at a time
29
+ ```
30
+
31
+ Do not guess a command name from this file. This skill covers *when to reach for
32
+ the CLI and how to read what comes back*; the catalog covers *what to type*.
33
+
34
+ ## Install and set up
35
+
36
+ `cratis` is a .NET tool published as `Cratis.Cli` with the command name
37
+ `cratis`. Install it with `dotnet tool install -g Cratis.Cli`, or through
38
+ Homebrew, or from a release binary. Follow with `cratis completions install`.
39
+
40
+ Once per project, so every agent working it can reach the store:
41
+
42
+ ```bash
43
+ cratis init # detects the AI tools in use and writes project context
44
+ cratis init --refresh # re-capture after upgrading the CLI
45
+ ```
46
+
47
+ Two things worth knowing before running it:
48
+
49
+ - The embedded catalog is a **snapshot**, not a live lookup. After a CLI upgrade
50
+ it still describes the older surface; `cratis init` reports the mismatch and
51
+ `--refresh` fixes it.
52
+ - If the repository's instruction file is supplied by a shared package or a
53
+ generated adapter, pass `--no-context`. Keep project-specific Chronicle
54
+ connection guidance in a repository-owned project context file; never patch
55
+ packaged or generated bytes.
56
+
57
+ ## Reach the right server
58
+
59
+ The connection string resolves in this exact order:
60
+
61
+ 1. `--server <CONNECTION_STRING>`
62
+ 2. the `CHRONICLE_CONNECTION_STRING` environment variable
63
+ 3. the active context's server in `~/.cratis/config.json`
64
+ 4. `chronicle://localhost:35000`
65
+
66
+ Credentials are composed separately, and only when the resolved string carries
67
+ no embedded `user:pass@` and no `apiKey=`: a cached access token first, then the
68
+ context's client id and secret.
69
+
70
+ Prefer a named context over repeating a connection string:
71
+
72
+ ```bash
73
+ cratis context create <name> --server chronicle://<host>:35000
74
+ cratis context set <name>
75
+ cratis context show
76
+ ```
77
+
78
+ A context also carries `--event-store` and `--namespace` defaults; both can be
79
+ overridden per command with `-e` and `-n`. The event store defaults to
80
+ `default` and the namespace to `Default`.
81
+
82
+ **Be deliberate about which store you are pointed at.** The same commands read
83
+ production and a local container, and several of them mutate. `cratis context
84
+ show` before anything that writes; `--debug` prints the resolved config path,
85
+ context, redacted connection string, and RPC timing to stderr.
86
+
87
+ ## Choose the output format
88
+
89
+ There is one output flag, `-o` / `--output`, with values `table`, `plain`,
90
+ `json`, `json-compact`, and `auto`. **There is no `--json` flag.**
91
+
92
+ - `auto` (the default) resolves to `json-compact` when an AI-agent environment
93
+ is detected, `plain` when `NO_COLOR` is set, `json` when stdout is redirected,
94
+ and `table` otherwise.
95
+ - `-q` / `--quiet` prints identifiers only, one per line — what you want when
96
+ piping one command into another. Combined with a JSON format it yields a JSON
97
+ array of identifiers.
98
+ - Prefer `plain` for large listings; JSON repeats every field name on every row.
99
+ Prefer `json`/`json-compact` for `show`/`get` commands where you need the
100
+ nested structure.
101
+
102
+ Exit codes are stable and worth branching on: `0` success, `1` not found, `2`
103
+ connection error, `3` server error, `4` authentication error, `5` validation
104
+ error.
105
+
106
+ ## Where to look
107
+
108
+ | Question | Where to look |
109
+ | --- | --- |
110
+ | Is the server healthy at all? | `cratis chronicle diagnose` (exit code 3 when unhealthy) |
111
+ | Why has this read model stopped updating? | `chronicle failed-partitions list --observer <id>` — the error is on the partition, not in the log |
112
+ | An observer is quarantined — why? | `chronicle failed-partitions show <observer> <partition> --detailed`, with attempt history |
113
+ | Did this event actually get appended? | `chronicle events get --event-type <type> --event-source-id <id>`, or `chronicle events tail` |
114
+ | What is this event's shape in the store? | `chronicle event-types list`, then `chronicle event-types show <type>` |
115
+ | Is this projection registered? | `chronicle projections list`, then `chronicle projections show <id>` |
116
+ | What does the read model actually hold? | `chronicle read-models get <read-model> <key>`, or `read-models instances` |
117
+ | Is a replay or migration still running? | `chronicle jobs list`, then `chronicle jobs get <id>` |
118
+ | What does the server itself recommend? | `chronicle recommendations list` |
119
+
120
+ Inspection commands are read-only: `diagnose`, `version`, `event-stores list`,
121
+ `namespaces list`, `event-types list|show`, `events get|tail`, `observers
122
+ list|show`, `failed-partitions list|show`, `projections list|show`,
123
+ `read-models list|instances|get|snapshots|occurrences`, `jobs list|get`,
124
+ `recommendations list`, `subscriptions list`, `identities list`, `users list`,
125
+ `applications list`, and `auth status`.
126
+
127
+ ## Read what comes back
128
+
129
+ - **A failed partition does not retry itself.** It stays failed until something
130
+ clears it, so a stale value is permanent rather than slow. That distinction is
131
+ the whole diagnosis: "not arrived yet" and "will never arrive" look identical
132
+ from the outside.
133
+ - **Fix the cause before replaying.** Replaying into an unfixed handler fails the
134
+ same way and buries the original error under a newer one.
135
+ - **A quarantined observer does not resume by itself.** Clearing the quarantine
136
+ is an explicit operation, and it is the last step, not the first.
137
+
138
+ ## Before you change anything
139
+
140
+ Default to read-only inspection. A request to diagnose a live store does not
141
+ authorize replay, retry, removal, quarantine clearing, or another mutation.
142
+
143
+ These commands mutate the running store:
144
+
145
+ - `chronicle observers replay`, `replay-partition`, `retry-partition`,
146
+ `clear-quarantine`
147
+ - `chronicle jobs stop`, `chronicle jobs resume`
148
+ - `chronicle recommendations perform`, `chronicle recommendations ignore`
149
+ - `chronicle subscriptions add`, `chronicle subscriptions remove`
150
+ - `chronicle users add`, `chronicle users remove`
151
+ - `chronicle applications add`, `chronicle applications remove`
152
+
153
+ Before any of them:
154
+
155
+ 1. Name the exact server context, event store, namespace, observer, partition,
156
+ event sequence, or job that will change.
157
+ 2. Capture its exact pre-state and the failure evidence that justifies the
158
+ operation.
159
+ 3. Prepare the repository-approved inverse or safe compensation when one exists;
160
+ stop when the effect cannot be safely reversed unless the owner separately
161
+ authorizes that irreversible operation.
162
+ 4. Show the dry run and obtain explicit authorization for that exact target and
163
+ action.
164
+ 5. Re-read the target immediately before mutation and stop on drift.
165
+
166
+ ### Two confirmation traps
167
+
168
+ **Confirmation prompts do not fire when stdout is not a terminal.** The CLI
169
+ treats a non-interactive session as an implicit yes, so a destructive command
170
+ run from a script, a pipeline, or an agent harness proceeds without `-y` and
171
+ without asking. Never rely on the prompt as the safety mechanism — the
172
+ authorization above is the safety mechanism.
173
+
174
+ **Not every mutating command prompts, even interactively.** `jobs stop` and
175
+ `jobs resume` describe themselves as prompting for confirmation, but no prompt
176
+ exists in the implementation. `users add`, `applications add`, and
177
+ `subscriptions add` do not prompt either.
178
+
179
+ Reaching for `--yes` to silence a prompt you have not read is how the wrong
180
+ store gets replayed. A failed partition you have not yet explained is not a
181
+ thing to clear: read it, fix the handler, then replay. Clearing it first
182
+ destroys the evidence and the same failure returns on the next event.
183
+
184
+ ## What the CLI cannot do
185
+
186
+ There is no `delete`, `purge`, `redact`, or `revise` command. Event redaction
187
+ and revision are browser Workbench operations. Treat a request to "remove an
188
+ event with the CLI" as a request that needs a different tool and a separate
189
+ authorization.
190
+
191
+ ## Related
192
+
193
+ - The terminal Workbench (`cratis chronicle workbench`) presents the same
194
+ read-only data as a navigable full-screen view; reach for it when you are
195
+ exploring rather than answering one question.
196
+ - The browser Workbench owns redaction, revision, and event-type authoring.
197
+ - Source-level diagnosis belongs in the application's own diagnostic workflow;
198
+ start there when the symptom reproduces locally, and start here when it is
199
+ only visible on a running server.
@@ -0,0 +1,2 @@
1
+ Copyright (c) Cratis. All rights reserved.
2
+ Licensed under the MIT license. See LICENSE file in the project root for full license information.
@@ -0,0 +1,433 @@
1
+ ---
2
+ name: cratis-chronicle-client-dotnet
3
+ description: Talk to a Chronicle server from a standalone .NET application with the Cratis.Chronicle client - connection strings, ChronicleClient construction outside any host, AddCratisChronicle for a worker or ASP.NET host, [EventType] records, IEventSequence.Append, reactors and reducers found by assembly scanning, the connection lifecycle and registration wait, and the client/server compatibility check. Use when a console, worker, or service app connects to Chronicle directly. Do not use for Arc applications, the Chronicle kernel, or the Kotlin, TypeScript, and Elixir clients.
4
+ license: MIT
5
+ ---
6
+
7
+ # The Chronicle client for .NET
8
+
9
+ `Cratis.Chronicle` is a **standalone client SDK**. A console app, a worker, or a
10
+ service constructs a client, asks it for an event store, and appends or observes.
11
+ There is no host requirement and no application architecture imposed.
12
+
13
+ > **This is not the Arc integration.** In an Arc application, Chronicle is wired
14
+ > in for you and commands return events that Arc appends. Everything below is the
15
+ > layer underneath: what an application that is *not* Arc has to do itself.
16
+
17
+ ## Verified product sources
18
+
19
+ This skill is verified against the `Cratis/Chronicle` repository at tag
20
+ `v17.0.1`, whose client sources are byte-identical to `16.45.2` for every public
21
+ type cited here except the compatibility check noted below.
22
+
23
+ | Package | Line | Purpose |
24
+ | --- | --- | --- |
25
+ | `Cratis.Chronicle` | `17.x` | `ChronicleClient`, `IEventStore`, `[EventType]`, observers |
26
+ | `Cratis.Chronicle.Connections` | `17.x` | `ChronicleConnectionString`, connection lifecycle |
27
+ | `Cratis.Chronicle.AspNetCore` | `17.x` | ASP.NET Core wiring, header/subdomain namespace resolvers |
28
+ | `Cratis.Chronicle.Testing` | `17.x` | in-process scenarios |
29
+ | `Cratis.Chronicle.CodeAnalysis` | `17.x` | the analyzers, a **separate opt-in** package |
30
+
31
+ Take the exact version from nuget.org. The version in source is a `1.0.0`
32
+ placeholder injected at pack time (`Source/Directory.Build.props`), so the
33
+ repository never carries the real number. Reverify before claiming support for a
34
+ version you have not checked.
35
+
36
+ ## Which package
37
+
38
+ ```shell
39
+ dotnet add package Cratis.Chronicle # console / worker service
40
+ dotnet add package Cratis.Chronicle.AspNetCore # ASP.NET Core
41
+ ```
42
+
43
+ `Cratis.Chronicle.CodeAnalysis` is **not** pulled in by `Cratis.Chronicle`. Add
44
+ it deliberately — it is what warns about the mistakes an event model makes
45
+ silently, such as a nullable event property.
46
+
47
+ ## Connecting
48
+
49
+ ### The smallest real thing
50
+
51
+ ```csharp
52
+ using Cratis.Chronicle;
53
+ using Cratis.Chronicle.Connections;
54
+
55
+ using var client = new ChronicleClient(ChronicleConnectionString.Development);
56
+ var eventStore = await client.GetEventStore("<EventStoreName>");
57
+ ```
58
+
59
+ That is the compiled documentation snippet verbatim
60
+ (`Documentation/client-snippets/get-started/console/connect.md`). Those snippet
61
+ files are compiled in CI against the real client projects, which makes them the
62
+ safest source of .NET Chronicle example code in existence — prefer them over
63
+ prose documentation.
64
+
65
+ `IChronicleClient` — `Source/Clients/DotNET/IChronicleClient.cs:11`:
66
+
67
+ | Member | Line |
68
+ | --- | --- |
69
+ | `ChronicleOptions Options { get; }` | `:16` |
70
+ | `ICausationManager CausationManager { get; }` | `:21` |
71
+ | `Task<IEventStore> GetEventStore(EventStoreName name, EventStoreNamespaceName? @namespace = default)` | `:32` |
72
+ | `Task<IEnumerable<EventStoreName>> GetEventStores(CancellationToken cancellationToken = default)` | `:39` |
73
+ | `void EvictEventStores()` | `:53` |
74
+
75
+ **There is no `IChronicleClient.Connect`.** Connecting happens inside
76
+ `GetEventStore`, which discovers artifacts and then connects
77
+ (`Source/Clients/DotNET/ChronicleClient.cs:403-409`). When you do need the
78
+ connection explicitly — in a worker's `ExecuteAsync`, for instance — it is
79
+ `eventStore.Connection.Connect()`.
80
+
81
+ `ChronicleClient` is `IDisposable`
82
+ (`Source/Clients/DotNET/ChronicleClient.cs:30`) with five public constructors
83
+ (`:76`, `:85`, `:94`, `:110`, `:183`). The one that matters for a real
84
+ application is `:110`:
85
+
86
+ ```csharp
87
+ public ChronicleClient(
88
+ ChronicleOptions options,
89
+ IClientArtifactsProvider? artifactsProvider = null,
90
+ IServiceProvider? serviceProvider = null,
91
+ IIdentityProvider? identityProvider = null,
92
+ ICorrelationIdAccessor? correlationIdAccessor = null,
93
+ IEventStoreNamespaceResolver? namespaceResolver = null,
94
+ ILoggerFactory? loggerFactory = null,
95
+ INamingPolicy? namingPolicy = null)
96
+ ```
97
+
98
+ The canonical standalone sample uses it — `Samples/SimpleConsole/Program.cs:28-33`:
99
+
100
+ ```csharp
101
+ var options = ChronicleOptions.FromConnectionString("chronicle://chronicle-dev-client:chronicle-dev-secret@localhost:35000");
102
+ options.DefaultSinkTypeId = sinkType;
103
+
104
+ using var client = new ChronicleClient(options, loggerFactory: loggerFactory);
105
+ var store = await client.GetEventStore("TestStoreCS");
106
+ ```
107
+
108
+ ### Options
109
+
110
+ `ChronicleOptions` — `Source/Clients/DotNET/ChronicleOptions.cs`. Statics:
111
+ `FromConnectionString(string)` (`:191`), `FromConnectionString(ChronicleConnectionString)`
112
+ (`:198`), `FromDevelopmentConnectionString()` (`:214`). The parameterless
113
+ constructor (`:40`) is the development connection string.
114
+
115
+ Options worth knowing: `AutoDiscoverAndRegister` (default `true`, `:90`),
116
+ `ConnectTimeout` (default 5 seconds, `:95`), `SkipKeepAlive` (`:127` — turn the
117
+ watchdog off for a short-lived client), `DefaultSinkTypeId` (default
118
+ `WellKnownSinkTypes.MongoDB`), `Tls`, `Authentication`, `RegistrationRetry`.
119
+
120
+ ### Connection strings, not URLs
121
+
122
+ **There is no `ChronicleUrl` type and no microservice id.** The identity of a
123
+ connection is a connection string plus an event store name and optionally a
124
+ namespace. `ChronicleConnectionString` lives in `Cratis.Chronicle.Connections`
125
+ (`Source/Clients/Connections/ChronicleConnectionString.cs:33`):
126
+
127
+ ```
128
+ chronicle://host[:port]/?opts
129
+ chronicle://user:pass@host[:port]/?opts
130
+ chronicle+srv://host/?opts
131
+ ```
132
+
133
+ Default port `35000`. `Default` is `chronicle://localhost:35000` (`:48`);
134
+ `Development` adds the `chronicle-dev-client` / `chronicle-dev-secret`
135
+ credentials (`:57`). Query options: `apiKey`, `auth`, `skipTlsValidation`,
136
+ `loadBalancer`, `srvNameServer`, `certificatePath`, `certificatePassword`
137
+ (`ChronicleConnectionStringBuilder.cs:27-36`). There is an implicit conversion
138
+ from `string` (`:158`) and a `Redacted` form for logging (`:152`).
139
+
140
+ > **Certificate validation is skipped by default, and that is deliberate.** The
141
+ > Chronicle server always serves TLS, generating a self-signed certificate on
142
+ > every start when none is configured, so a development pair connects with no
143
+ > setup — the rationale is written out at `ChronicleClient.cs:141-146`. **A
144
+ > production connection string must set `skipTlsValidation=false`** (or
145
+ > `Tls.SkipCertificateValidation = false`) against a server whose certificate is
146
+ > actually verifiable.
147
+
148
+ ### In a host
149
+
150
+ ```csharp
151
+ var builder = Host.CreateApplicationBuilder(args);
152
+ builder.AddCratisChronicle(options => options.EventStore = "<EventStoreName>");
153
+ builder.Services.AddHostedService<<WorkerName>>();
154
+ await builder.Build().RunAsync();
155
+ ```
156
+
157
+ `AddCratisChronicle` is an extension on **`IHostApplicationBuilder`**
158
+ (`Source/Clients/DotNET/ChronicleHostApplicationBuilderExtensions.cs:28`) and binds
159
+ the `Cratis:Chronicle` configuration section by default (`:25`, `:37`), with
160
+ `ValidateDataAnnotations().ValidateOnStart()`. The bound type is
161
+ `ChronicleClientOptions : ChronicleOptions`, which adds a `[Required] EventStore`
162
+ and an optional `EventStoreNamespaceResolverType`.
163
+
164
+ > **`AddCratisChronicle` on `IServiceCollection` does not exist**, despite what
165
+ > `Documentation/clients/dotnet/getting-started.md:61` shows. The real extensions
166
+ > are on `IHostApplicationBuilder`, `WebApplicationBuilder` (in the AspNetCore
167
+ > package), and Aspire's `IDistributedApplicationBuilder`. `IHostBuilder.AddCratisChronicle()`
168
+ > also exists but only registers concept type converters
169
+ > (`Source/Clients/DotNET/Hosting/HostBuilderExtensions.cs:18-23`) — it is not
170
+ > the wiring entry point.
171
+
172
+ `IChronicleBuilder` extensions are exactly five:
173
+ `WithArtifactsProvider`, `WithIdentityProvider`, `WithCorrelationIdAccessor`,
174
+ `WithNamespaceResolver`, `WithCamelCaseNamingPolicy`
175
+ (`Source/Clients/DotNET/ChronicleBuilderExtensions.cs`). **There is no
176
+ `WithClaimsBasedNamespaceResolver`**, despite a doc comment at
177
+ `ChronicleOptions.cs:141` referring to one. Pass the resolver instead:
178
+ `new ChronicleClient(options, namespaceResolver: new ClaimsBasedNamespaceResolver("tenant_id"))`.
179
+
180
+ ## The client and the server check each other
181
+
182
+ Since Chronicle 17 the compatibility check is a server-side RPC, and **the client
183
+ runs it automatically inside `Connect()`** —
184
+ `Source/Clients/Connections/ChronicleConnection.cs:310` calls
185
+ `CheckCompatibility` (`:405`), sending the client type, client version, protocol
186
+ version, and the descriptor set its contracts package was built with (`:411-417`).
187
+ The rationale is at `:400-404`: Chronicle has clients in four languages and only
188
+ some can build a descriptor set at runtime, so each ships the one it was built
189
+ with and the server does the single comparison.
190
+
191
+ Behavior you can rely on:
192
+
193
+ - A server too old to have the RPC answers `Unimplemented`, and the client falls
194
+ back to the previous client-side exchange (`:419-425`) — upgrading the client
195
+ does not silently drop the check.
196
+ - Any other transport error is **logged and ignored** (`:426-432`), on the stated
197
+ reasoning that failing to ask says nothing about whether the two sides agree.
198
+ - A genuine mismatch throws `IncompatibleServerException`
199
+ (`Source/Clients/Connections/IncompatibleServerException.cs:10`) whose message
200
+ names the server address, its version, its protocol version, and the specific
201
+ incompatibilities (`:436-443`).
202
+
203
+ The client identifies itself as `".NET"`
204
+ (`Source/Clients/Connections/ChronicleClientIdentity.cs:22`) with its assembly
205
+ informational version (`:27`) and the contracts protocol version (`:32`).
206
+
207
+ ## Event types
208
+
209
+ ```csharp
210
+ using Cratis.Chronicle.Events;
211
+
212
+ /// <summary><What happened, in the past tense.></summary>
213
+ [EventType]
214
+ public record <EventName>(<Type> <Property>);
215
+ ```
216
+
217
+ `EventTypeAttribute` is in `Cratis.Chronicle.Events` —
218
+ `Source/Clients/DotNET/Events/EventTypeAttribute.cs:20`:
219
+
220
+ ```csharp
221
+ public sealed class EventTypeAttribute(string id = "", uint generation = EventTypeGeneration.FirstValue) : Attribute
222
+ ```
223
+
224
+ **Two arguments only** — `id` (empty means the type name) and `generation`
225
+ (`EventTypeGeneration.FirstValue` is `1U`). There is no `isPublic` parameter.
226
+ `AttributeUsage` is `AttributeTargets.Class`, which records satisfy.
227
+
228
+ For a new event, pass nothing: the type name is the identifier. Use `generation`
229
+ only when evolving a contract that already exists, and prefer
230
+ `EventTypeGenerationForAttribute<TEventType>` over a second `[EventType]` — the
231
+ guidance is in the attribute's own remarks (`EventTypeAttribute.cs:14-18`).
232
+
233
+ ## Appending
234
+
235
+ `IEventLog : IEventSequence` is a marker
236
+ (`Source/Clients/DotNET/EventSequences/IEventLog.cs:9`); the surface is on
237
+ `IEventSequence` (`Source/Clients/DotNET/EventSequences/IEventSequence.cs:14`):
238
+
239
+ ```csharp
240
+ Task<AppendResult> Append( // :115
241
+ EventSourceId eventSourceId,
242
+ object @event,
243
+ EventStreamType? eventStreamType = default,
244
+ EventStreamId? eventStreamId = default,
245
+ EventSourceType? eventSourceType = default,
246
+ CorrelationId? correlationId = default,
247
+ IEnumerable<string>? tags = default,
248
+ ConcurrencyScope? concurrencyScope = default,
249
+ DateTimeOffset? occurred = default,
250
+ Subject? subject = default);
251
+
252
+ Task<AppendManyResult> AppendMany(EventSourceId eventSourceId, IEnumerable<object> events, /* same optionals */); // :144
253
+ Task<AppendManyResult> AppendMany(IEnumerable<EventForEventSourceId> events, /* ... */); // :167
254
+ ```
255
+
256
+ ```csharp
257
+ var result = await eventStore.EventLog.Append(<eventSourceId>, new <EventName>(<value>));
258
+ if (!result.IsSuccess)
259
+ {
260
+ // result.ConstraintViolations, result.ConcurrencyViolation, result.Errors
261
+ }
262
+ ```
263
+
264
+ - `EventSourceId` is a `record EventSourceId(string Value) : ConceptAs<string>`
265
+ with implicit conversion from `string` and `Guid`
266
+ (`Source/Clients/DotNET/Events/EventSourceId.cs:12`, `:37`, `:44`).
267
+ - `AppendResult` carries `SequenceNumber`, `IsSuccess`,
268
+ `HasConstraintViolations`, `HasConcurrencyViolations`, `HasErrors`,
269
+ `ConstraintViolations`, `ConcurrencyViolation`, `Errors`, and the routing facts
270
+ (`Source/Clients/DotNET/EventSequences/AppendResult.cs:13-59`).
271
+ - The `IEnumerable<EventForEventSourceId>` overload is the way to append across
272
+ several event sources in one batch.
273
+ - **There is no `AppendAnonymous`.** It does not exist anywhere in the product.
274
+
275
+ Reading back is on the same interface: `GetForEventSourceIdAndEventTypes` (`:51`),
276
+ `HasEventsFor` (`:58`), `GetFromSequenceNumber` (`:67`), `GetNextSequenceNumber`
277
+ (`:73`), `GetTailSequenceNumber` (`:84`). Erasure is `Redact` (`:191`, `:200`).
278
+
279
+ Several appends as one unit:
280
+
281
+ ```csharp
282
+ var unitOfWork = eventStore.UnitOfWorkManager.Begin(CorrelationId.New());
283
+ await eventStore.EventLog.Transactional.Append(<eventSourceId>, new <EventName>(<value>));
284
+ await unitOfWork.Commit();
285
+ ```
286
+
287
+ `ITransactionalEventSequence` returns `Task`, not a result
288
+ (`Source/Clients/DotNET/EventSequences/ITransactionalEventSequence.cs:33`); the
289
+ results live on the unit of work.
290
+
291
+ ## Observing
292
+
293
+ | Artifact | Shape | Source |
294
+ | --- | --- | --- |
295
+ | Reactor | marker `IReactor` (empty interface), `[Reactor]` optional | `Reactors/IReactor.cs:9`, `Reactors/ReactorAttribute.cs:14` |
296
+ | Reducer | `IReducerFor<TReadModel>` | `Reducers/IReducerFor.cs:10` |
297
+ | Projection | `IProjectionFor<TReadModel>` with `Define(IProjectionBuilderFor<TReadModel>)` | `Projections/IProjectionFor.cs:10-17` |
298
+ | Model-bound projection | attributes on the read model | `Projections/ModelBound/` |
299
+ | Read model reactor | marker `IReadModelReactor`, methods `Added`/`Modified`/`Removed` | `ReadModels/IReadModelReactor.cs:10-18` |
300
+
301
+ **A reactor handler is found by its first parameter's type**, and further
302
+ parameters are resolved as dependencies — `EventContext`, a read model, a service
303
+ (`Source/Clients/DotNET/Reactors/EventHandlerMethods.cs:21-46`, `:89-103`). The
304
+ return may be `Task`, `void`, or a side-effect event type.
305
+
306
+ ```csharp
307
+ using Cratis.Chronicle.Events;
308
+ using Cratis.Chronicle.Reactors;
309
+
310
+ public class <ReactorName>(<IDependency> <dependency>) : IReactor
311
+ {
312
+ public async Task <AnyMethodName>(<EventName> @event, EventContext context)
313
+ {
314
+ await <dependency>.<DoSomething>(context.EventSourceId, @event.<Property>);
315
+ }
316
+ }
317
+ ```
318
+
319
+ > **There is no `[Observer]` attribute and no `IObserver` client artifact.**
320
+ > "Observer" is the kernel's umbrella term for a projection, reducer, or reactor;
321
+ > it is not something you write. `Source/Clients/DotNET/Observation/` holds
322
+ > `ObserverId`, `ObserverRunningState`, and failed-partition types only.
323
+
324
+ Read models are queried through `eventStore.ReadModels`
325
+ (`Source/Clients/DotNET/ReadModels/IReadModels.cs`):
326
+ `GetInstanceById<TReadModel>(ReadModelKey, ReadModelSessionId?)` (`:38`),
327
+ `GetInstances<TReadModel>(EventCount?)` (`:55`),
328
+ `GetSnapshotsById<TReadModel>` (`:63`),
329
+ `IObservable<ReadModelChangeset<TReadModel>> Watch<TReadModel>()` (`:70`).
330
+
331
+ ## Discovery
332
+
333
+ Artifacts are found by **assembly scanning**, with no registration call and no DI
334
+ container required. `DefaultClientArtifactsProvider.Default` composes the
335
+ project-referenced and package-referenced assemblies
336
+ (`Source/Clients/DotNET/DefaultClientArtifactsProvider.cs:35`), and the
337
+ predicates are exactly (`:230-238`):
338
+
339
+ | Kind | Predicate |
340
+ | --- | --- |
341
+ | event types | `HasAttribute<EventTypeAttribute>()` or `HasAttribute<EventTypeGenerationForAttribute>()` |
342
+ | projections | `HasInterface(typeof(IProjectionFor<>))` |
343
+ | model-bound projections | `HasModelBoundProjectionAttributes()` |
344
+ | reactors | `HasInterface<IReactor>()` and not generic |
345
+ | read model reactors | `HasInterface<IReadModelReactor>()` and not generic |
346
+ | reducers | `HasInterface(typeof(IReducerFor<>))` and not generic |
347
+
348
+ Explicit registration is available per family as an alternative —
349
+ `IEventTypes.Register`, `IConstraints.Register`, `IProjections.Register`,
350
+ `IReducers.Register`, `IReactors.Register<TReactor>()`,
351
+ `IReadModels.Register<TReadModel>()` — and is what you use with
352
+ `AutoDiscoverAndRegister = false`.
353
+
354
+ ## Lifecycle
355
+
356
+ - **Registration is wired to the connection, not called by you.**
357
+ `EventStore.cs:265-268` subscribes `RegisterAll` to `Connection.Lifecycle.OnConnected`
358
+ when `autoDiscoverAndRegister` is on, so a reconnect re-registers everything.
359
+ - `DiscoverAll()` does event types first, then constraints, reactors, reducers,
360
+ projections, and seeding in parallel (`EventStore.cs:349-362`). `RegisterAll()`
361
+ is single-flighted with jittered backoff and a background retry loop (`:373-379`).
362
+ - **Wait with `WaitForRegistration`, not by polling `IsConnected`.**
363
+ `RegistrationWaitExtensions.WaitForRegistration(this IEventStore, TimeSpan? timeout = default)`
364
+ (`Source/Clients/DotNET/Registrations/RegistrationWaitExtensions.cs:42`, default
365
+ 5 seconds) exists precisely for this, and its own remarks warn against the
366
+ `IConnectionLifecycle.IsConnected` alternative (`:34-39`). Connected is not
367
+ registered.
368
+ - **Keepalive is a bidirectional stream plus a watchdog.** The watchdog monitors
369
+ every `MonitorIntervalMilliseconds = 1000`
370
+ (`Source/Clients/Connections/ConnectionWatchdog.cs:34`) and a keepalive that
371
+ falls more than 5 seconds behind is treated as a lost connection, with
372
+ reconnect backoff capped at 30 seconds. **The failure mode is silence, not an
373
+ exception** — appends keep working while observers go quiet.
374
+ - `Dispose()` disposes read model reactors for created stores, cancels the owned
375
+ connection, and disposes the connection
376
+ (`Source/Clients/DotNET/ChronicleClient.cs:225-246`).
377
+
378
+ ## What a standalone app owns that a host would have supplied
379
+
380
+ Every one of these has a silent default. Read them as a checklist, because the
381
+ defaults are reasonable for a sample and wrong for a service
382
+ (`ChronicleClient.cs:121-127`):
383
+
384
+ | Concern | Default when you pass nothing |
385
+ | --- | --- |
386
+ | `IClientArtifactsProvider` | `DefaultClientArtifactsProvider.Default` — full assembly scan |
387
+ | `IServiceProvider` | `DefaultServiceProvider`, which activates everything through `Activator.CreateInstance` (`DefaultServiceProvider.cs:41`) |
388
+ | `IIdentityProvider` | `BaseIdentityProvider` |
389
+ | `ICorrelationIdAccessor` | `CorrelationIdAccessor` |
390
+ | `IEventStoreNamespaceResolver` | `DefaultEventStoreNamespaceResolver` — always `"Default"` |
391
+ | `ILoggerFactory` | `new LoggerFactory()` — **a silent one** |
392
+ | Configuration binding | none; there is no `Cratis:Chronicle` section without a host |
393
+
394
+ The `IServiceProvider` default is the sharp one: **a reactor or reducer with
395
+ constructor dependencies is default-constructed** unless you pass a real
396
+ container. Pass one, or keep observers dependency-free.
397
+
398
+ The AspNetCore package additionally registers `AddUnitOfWork()`,
399
+ `AddCompliance()`, `AddCausation()`, `AddChronicleHealthCheck()`, and
400
+ `UseCratisChronicle()` — none of which the base package gives you.
401
+
402
+ **Keep the process alive.** Observation is a live gRPC duplex stream; a console
403
+ app that appends and returns from `Main` never sees a reactor run.
404
+
405
+ ## Common pitfalls
406
+
407
+ | Pitfall | Why it bites |
408
+ | --- | --- |
409
+ | Looking for `IChronicleClient.Connect` | It does not exist; `GetEventStore` connects, and `eventStore.Connection.Connect()` is the explicit form |
410
+ | `services.AddCratisChronicle(...)` | Not a real extension; it is on `IHostApplicationBuilder`/`WebApplicationBuilder` |
411
+ | `builder.WithClaimsBasedNamespaceResolver()` | Not a real extension; pass the resolver to the client instead |
412
+ | Shipping the default TLS behavior to production | Certificate validation is skipped by default |
413
+ | Constructing a client without an `ILoggerFactory` | You get a silent logger and lose every diagnostic the client emits |
414
+ | A reactor with constructor dependencies and no `IServiceProvider` | It is built by `Activator.CreateInstance` with no arguments |
415
+ | Polling `IsConnected` to know it is ready | Connected is not registered; use `WaitForRegistration` |
416
+ | Treating a quiet reactor as "no events yet" | Keepalive loss stops observers while appends keep succeeding |
417
+ | Returning from `Main` after appending | The observation stream dies with the process |
418
+ | Expecting the analyzers | `Cratis.Chronicle.CodeAnalysis` is a separate opt-in package |
419
+ | Copying a version out of the repository | It is a `1.0.0` placeholder; take it from nuget.org |
420
+
421
+ ## Verify
422
+
423
+ - The package version is the one you intended, taken from nuget.org.
424
+ - The connection succeeds and no `IncompatibleServerException` is thrown — and if
425
+ the compatibility check was skipped, you know why (a transport error is logged
426
+ and ignored by design).
427
+ - A production connection string sets `skipTlsValidation=false`.
428
+ - An `ILoggerFactory` is supplied, and client logs are visible.
429
+ - Observers that take dependencies get a real `IServiceProvider`.
430
+ - Readiness is established with `WaitForRegistration`, not with `IsConnected`.
431
+ - The process stays alive for as long as observation is expected.
432
+ - The build is clean and the specifications pass against the verified package
433
+ version.
@@ -0,0 +1,2 @@
1
+ Copyright (c) Cratis. All rights reserved.
2
+ Licensed under the MIT license. See LICENSE file in the project root for full license information.