@r0kshan/cairn 1.0.0-RC12 → 1.0.0-RC14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -17,6 +17,7 @@ Cairn is an [Elkjs (Eclipse Layout Kernel)](https://github.com/kieler/elkjs) bas
17
17
  - [Preview](#preview)
18
18
  - [Installation](#installation)
19
19
  - [Commands](#commands)
20
+ - [License](#license)
20
21
  - [More](#more)
21
22
 
22
23
  ## Why cairn?
@@ -32,6 +33,7 @@ Furthermore, complexe software architecture with many flows and component genera
32
33
  | **Spacial optimization** | Cairn aims to optimize space as much as possible (Still working on improving this functionnality) |
33
34
  | **Typed diagrams with validation.** | Each view defines its element kinds and rules; `cairn validate` reports syntax, schema, and completeness issues as source-located, coded diagnostics, with a JSON mode for CI. |
34
35
  | **Flow matrix.** | `cairn matrix` exports the flow matrix as CSV, Markdown, or SVG — from any view, with the columns that view can fill. In infrastructure it splits protocol from port and annotates endpoints that sit in a network zone; headers localise via `style { lang: fr }`. The same table is available to embedders through `compile(source, { matrix: true })`. |
36
+ | **Author-controlled positioning.** | Layout is automatic, with three opt-in overrides for when it gets a diagram wrong: `order: 2` sequences a top-level element along the disposition's own reading direction (left to right in `wide`, top to bottom in `tall`) and sorts siblings inside a container, `A.right -> B.left` pins which side of an element a flow leaves and arrives on, and the arrow glyph carries the line style (`->` solid, `-->` dashed, `..>` dotted). |
35
37
  | **Enterprise-view extras.** | Business objects on flows, an auto-generated legend, and a numbered-flow table via `flow-text: numbered`. |
36
38
  | **French or English output.** | `style { lang: fr }` localizes band titles, legend, and matrix headers while keeping keywords English for portable sources (open to adding other languages if you find this usefull) |
37
39
  | **In-built themes and customizable colours** | Whether using the default or a chosen in-built theme, element colours can be overriden for all elements of a given kind in the `style` block |
@@ -56,15 +58,10 @@ As a result here's a comparaison of the same diagram done with D2 (ELK Layout) v
56
58
  </tr>
57
59
  <tr>
58
60
  <td>I encountered overlapping issues for which I couldn't find a workaround</td>
59
- <td>The overlapping issues have been addressed. A caveat remains: the long-distance arrow can affect readability (still working on improvements)</td>
61
+ <td>The overlapping labels have been addressed.</td>
60
62
  </tr>
61
63
  </table>
62
64
 
63
- ## Known limitations I'm working on
64
-
65
- * While Cairn ensures 0 label overlap, flows on large diagrams still take a longer than neccessary route to the target element - issue [#26](https://github.com/R0kshan/cairn/issues/26) created to adress this
66
- * At the moment Cairn doesn't give the user the possibility to control positioning - issue [#8](https://github.com/R0kshan/cairn/issues/8) created to adress this
67
-
68
65
  ## Usage
69
66
 
70
67
  Either use the cli or the [ playground](https://cairn-psi-five.vercel.app/).
@@ -87,6 +84,25 @@ Every image below is rendered by cairn CLI from a `.cairn` source in [`examples/
87
84
  <p align="center"><img src="examples/application-medium.svg" alt="Medium application view" width="760"></p>
88
85
  <p align="center"><img src="examples/application-large.svg" alt="Large application view" width="760"></p>
89
86
 
87
+ #### Technology logos
88
+
89
+ ```cairn
90
+ application STOREFRONT "Storefront" { logo: nextdotjs
91
+ module WEB "Web client" { logo: react }
92
+ }
93
+ queue EVENTS "Order events" { logo: apachekafka }
94
+ datastore ORDER_DB "Order store" { logo: postgresql }
95
+ module BILLING "Billing" { logo: "./logos/acme.svg" }
96
+ ```
97
+
98
+ <p align="center"><img src="examples/application-tech-stack.svg" alt="Application view with technology logos" width="760"></p>
99
+
100
+ Six of the 37 built-in logos keep their own licence rather than simple-icons'
101
+ CC0-1.0. A diagram that draws one embeds that artwork, so the SVG carries an
102
+ attribution comment naming the mark, its source and its licence — keep it in
103
+ the file, and check that an SVG optimiser is not set to strip comments. The
104
+ other 31 add nothing. See [THIRD-PARTY-NOTICES.md](./THIRD-PARTY-NOTICES.md).
105
+
90
106
  ### Infrastructure view diagram examples from small to large
91
107
 
92
108
  <p align="center"><img src="examples/infrastructure-small.svg" alt="Small infrastructure view" width="760"></p>
@@ -103,7 +119,7 @@ Colours are resolved at three levels — most specific wins. From lowest to high
103
119
 
104
120
  1. **Theme defaults** — per-kind colours defined by the selected theme (fill, stroke, text for each element kind).
105
121
  2. **Diagram-level per-kind overrides** — override colours for all elements of a given kind in the `style` block:
106
- ```
122
+ ```cairn
107
123
  style {
108
124
  fill actor-group: #eef4fb
109
125
  stroke actor-group: #7a9cc4 dashed
@@ -112,12 +128,12 @@ Colours are resolved at three levels — most specific wins. From lowest to high
112
128
  }
113
129
  ```
114
130
  3. **Inline per-element styles** — override colour for a single element:
115
- ```
131
+ ```cairn
116
132
  block API "API gateway" { style { fill: #e8f5e9 stroke: #2e7d32 text: #1b5e20 } }
117
133
  ```
118
134
 
119
135
  Flows can also be coloured inline:
120
- ```
136
+ ```cairn
121
137
  COM_CTR -> OBS : "Alerts…" { label: above stroke: dashed #a33 text: #a33 }
122
138
  ```
123
139
 
@@ -125,11 +141,89 @@ See [`examples/colors-custom.cairn`](examples/colors-custom.cairn) for a full ex
125
141
 
126
142
  <p align="center"><img src="examples/colors-custom.svg" alt="Custom colours" width="620"></p>
127
143
 
144
+ ### Positioning and flow line styles
145
+
146
+ **Line style** — carried by the arrow glyph:
147
+
148
+ ```cairn
149
+ TRIAGE -> TASKS (MQ, JSON) # solid — the default
150
+ SCORING --> REINSURER (SFTP, CSV) # dashed
151
+ TRIAGE ..> REINSURER (API_REST, JSON) # dotted
152
+ ```
153
+
154
+ **Attachment sides** — pin either endpoint, or both, with `left`, `right`,
155
+ `top` or `bottom`:
156
+
157
+ ```cairn
158
+ FORM.right -> TRIAGE.left (API_REST, JSON) # both ends pinned
159
+ SCORING -> TASKS.right (MQ, JSON) # only the arrival pinned
160
+ SCORING.top -> TRIAGE.top (API_REST, JSON) # a backward flow, pinned both ends
161
+ ```
162
+
163
+ <p align="center"><img src="examples/positioning-sides.svg" alt="Flow attachment sides" width="760"></p>
164
+
165
+ **Element ordering** — `order:` is a statement in the element's own body, not a
166
+ `style` entry. Lower comes first along the disposition's reading direction: left
167
+ to right in `wide`/`slide`, top to bottom in `tall`/`page`. From
168
+ [`examples/placement/reading-order.cairn`](examples/placement/reading-order.cairn):
169
+
170
+ ```cairn
171
+ application BACKEND_L1 "Line 1 backend" {
172
+ order: 1
173
+ module MSG_L1 "Messaging\nhandler"
174
+ }
175
+ application BACKEND_L2 "Line 2 backend" {
176
+ order: 2
177
+ module MSG_L2 "Messaging\nhandler"
178
+ }
179
+ ```
180
+
181
+ <p align="center"><img src="examples/placement/reading-order.svg" alt="Top-level elements sequenced by order:" width="620"></p>
182
+
183
+ Inside a container the axis flips: `order:` sorts the siblings sharing a layer —
184
+ top to bottom in `wide`, left to right in `tall`. From
185
+ [`examples/positioning.cairn`](examples/positioning.cairn):
186
+
187
+ ```cairn
188
+ actor-group STAFF "Payment actors" {
189
+ actor OPERATOR "Payment operator" { order: 1 }
190
+ actor AUDITOR "Compliance auditor" { order: 2 }
191
+ }
192
+ ```
193
+
194
+ <p align="center"><img src="examples/positioning.svg" alt="Element ordering, pinned sides and arrow glyphs" width="760"></p>
195
+
196
+ More examples: [`examples/placement/`](examples/placement/) shows one shape four
197
+ ways (`baseline.cairn`, `sides.cairn`, `reading-order.cairn`,
198
+ `flow-label.cairn`), and [`examples/dispositions/`](examples/dispositions/) ships
199
+ the attachment-sides file as `wide`, `tall`, `slide` and `page` variants.
200
+
201
+ **Label position** — this one *is* style, so it lives in the `style` block and
202
+ resolves at the usual three levels (view default, diagram, inline per flow). From
203
+ [`examples/placement/flow-label.cairn`](examples/placement/flow-label.cairn):
204
+
205
+ ```cairn
206
+ style {
207
+ flow-label: above # on-line (default) | above | below
208
+ }
209
+
210
+ AGENT -> RECEIVE : "File a claim" # above, from the style block
211
+ ASSIGN -> SETTLE : "Request settlement" { label: below } # overridden per flow
212
+ TRACE -> STORE : "Archive the trace" { label: on-line } # back to the default
213
+ ```
214
+
215
+ <p align="center"><img src="examples/placement/flow-label.svg" alt="Flow label positions" width="760"></p>
216
+
217
+ `on-line` centres the text on the run behind a halo; `above`/`below` lift it clear
218
+ by a fixed offset first. Either way the label is settled afterwards, so the
219
+ zero-overlap gate holds. Every other diagram in this README shows the `on-line`
220
+ default.
221
+
128
222
  ### Dispositions
129
223
 
130
224
  Same diagram different disposition :
131
- - [Slide disposition](https://cairn-psi-five.vercel.app/#src=ZGlhZ3JhbSBsb2dpY2FsICJPbmxpbmUgYXBwb2ludG1lbnQgYm9va2luZyDigJQgbG9naWNhbCB2aWV3IgpzdHlsZSB7IGRpc3Bvc2l0aW9uOiBzbGlkZSB9CgphY3Rvci1ncm91cCBVU0VSUyAiVXNlcnMiIHsKICBhY3RvciBQQVRJRU5UICJQYXRpZW50Igp9CgphY3Rvci1ncm91cCBTVEFGRiAiU3RhZmYiIHsKICBhY3RvciBTRUNSRVRBUlkgIk1lZGljYWwgc2VjcmV0YXJ5Igp9CgpzeXN0ZW0gQk9PS0lORyAiQXBwb2ludG1lbnQgYm9va2luZyBzeXN0ZW0iIHsKICBsYXllciBGUk9OVCAiUHJlc2VudGF0aW9uIGxheWVyIiB7CiAgICBibG9jayBQT1JUQUwgIkJvb2tpbmdcbnBvcnRhbCIKICB9CiAgbGF5ZXIgQlVTSU5FU1MgIkJ1c2luZXNzIGxheWVyIiB7CiAgICBibG9jayBTQ0hFRFVMRVIgIlNsb3Rcbm1hbmFnZW1lbnQiCiAgICBibG9jayBOT1RJRiAiTm90aWZpY2F0aW9ucyIKICB9Cn0KCmV4dGVybmFsIEVYVCAiRXh0ZXJuYWwgc3lzdGVtcyIgewogIGJsb2NrIFNNUyAiU01TIGdhdGV3YXkiCn0KCiMgLS0tLSBidXNpbmVzcyBvYmplY3RzIC0tLS0KYnVzaW5lc3Mtb2JqZWN0IEJPX0FQUFQgIkFwcG9pbnRtZW50IiAic2xvdCBib29rZWQgYnkgYSBwYXRpZW50IHdpdGggYSBwcmFjdGl0aW9uZXIiCmJ1c2luZXNzLW9iamVjdCBCT19TTE9UICJTbG90IiAidGltZSB3aW5kb3cgb3BlbiBmb3IgYm9va2luZyIKClBBVElFTlQgLT4gUE9SVEFMIDogIlNlYXJjaCBhIHNsb3RcbmFuZCBib29rIiBbQk9fU0xPVF0KUE9SVEFMIC0+IFNDSEVEVUxFUiA6ICJCb29raW5nIHJlcXVlc3QiIFtCT19BUFBUXQpTQ0hFRFVMRVIgLT4gTk9USUYgOiAiQXBwb2ludG1lbnQgY29uZmlybWVkIiBbQk9fQVBQVF0KTk9USUYgLT4gU01TIDogIlNlbmQgU01TIHJlbWluZGVyIgpTRUNSRVRBUlkgLT4gU0NIRURVTEVSIDogIk9wZW4gLyBibG9ja1xuc2xvdHMiIFtCT19TTE9UXQpTQ0hFRFVMRVIgLT4gUEFUSUVOVCA6ICJBcHBvaW50bWVudFxuY29uZmlybWF0aW9uIiBbQk9fQVBQVF0KCmxlZ2VuZCB7CiAgbm90ZSAiSGVhbHRoIGRhdGEgaXMgc3RvcmVkIG9uIGNlcnRpZmllZCBoZWFsdGgtZGF0YS1ob3N0aW5nIGluZnJhc3RydWN0dXJlIgp9Cg==)
132
- - [Page disposition](https://cairn-psi-five.vercel.app/#src=ZGlhZ3JhbSBsb2dpY2FsICJPbmxpbmUgYXBwb2ludG1lbnQgYm9va2luZyDigJQgbG9naWNhbCB2aWV3IgpzdHlsZSB7IGRpc3Bvc2l0aW9uOiBwYWdlIH0KCmFjdG9yLWdyb3VwIFVTRVJTICJVc2VycyIgewogIGFjdG9yIFBBVElFTlQgIlBhdGllbnQiCn0KCmFjdG9yLWdyb3VwIFNUQUZGICJTdGFmZiIgewogIGFjdG9yIFNFQ1JFVEFSWSAiTWVkaWNhbCBzZWNyZXRhcnkiCn0KCnN5c3RlbSBCT09LSU5HICJBcHBvaW50bWVudCBib29raW5nIHN5c3RlbSIgewogIGxheWVyIEZST05UICJQcmVzZW50YXRpb24gbGF5ZXIiIHsKICAgIGJsb2NrIFBPUlRBTCAiQm9va2luZ1xucG9ydGFsIgogIH0KICBsYXllciBCVVNJTkVTUyAiQnVzaW5lc3MgbGF5ZXIiIHsKICAgIGJsb2NrIFNDSEVEVUxFUiAiU2xvdFxubWFuYWdlbWVudCIKICAgIGJsb2NrIE5PVElGICJOb3RpZmljYXRpb25zIgogIH0KfQoKZXh0ZXJuYWwgRVhUICJFeHRlcm5hbCBzeXN0ZW1zIiB7CiAgYmxvY2sgU01TICJTTVMgZ2F0ZXdheSIKfQoKIyAtLS0tIGJ1c2luZXNzIG9iamVjdHMgLS0tLQpidXNpbmVzcy1vYmplY3QgQk9fQVBQVCAiQXBwb2ludG1lbnQiICJzbG90IGJvb2tlZCBieSBhIHBhdGllbnQgd2l0aCBhIHByYWN0aXRpb25lciIKYnVzaW5lc3Mtb2JqZWN0IEJPX1NMT1QgIlNsb3QiICJ0aW1lIHdpbmRvdyBvcGVuIGZvciBib29raW5nIgoKUEFUSUVOVCAtPiBQT1JUQUwgOiAiU2VhcmNoIGEgc2xvdFxuYW5kIGJvb2siIFtCT19TTE9UXQpQT1JUQUwgLT4gU0NIRURVTEVSIDogIkJvb2tpbmcgcmVxdWVzdCIgW0JPX0FQUFRdClNDSEVEVUxFUiAtPiBOT1RJRiA6ICJBcHBvaW50bWVudCBjb25maXJtZWQiIFtCT19BUFBUXQpOT1RJRiAtPiBTTVMgOiAiU2VuZCBTTVMgcmVtaW5kZXIiClNFQ1JFVEFSWSAtPiBTQ0hFRFVMRVIgOiAiT3BlbiAvIGJsb2NrXG5zbG90cyIgW0JPX1NMT1RdClNDSEVEVUxFUiAtPiBQQVRJRU5UIDogIkFwcG9pbnRtZW50XG5jb25maXJtYXRpb24iIFtCT19BUFBUXQoKbGVnZW5kIHsKICBub3RlICJIZWFsdGggZGF0YSBpcyBzdG9yZWQgb24gY2VydGlmaWVkIGhlYWx0aC1kYXRhLWhvc3RpbmcgaW5mcmFzdHJ1Y3R1cmUiCn0K)
225
+ - [Slide disposition](https://cairn-psi-five.vercel.app/#src=ZGlhZ3JhbSBsb2dpY2FsICJPbmxpbmUgYXBwb2ludG1lbnQgYm9va2luZyDigJQgbG9naWNhbCB2aWV3IgpzdHlsZSB7IGRpc3Bvc2l0aW9uOiBzbGlkZSB9CgphY3Rvci1ncm91cCBVU0VSUyAiVXNlcnMiIHsKICBhY3RvciBQQVRJRU5UICJQYXRpZW50Igp9CgphY3Rvci1ncm91cCBTVEFGRiAiU3RhZmYiIHsKICBhY3RvciBTRUNSRVRBUlkgIk1lZGljYWwgc2VjcmV0YXJ5Igp9CgpzeXN0ZW0gQk9PS0lORyAiQXBwb2ludG1lbnQgYm9va2luZyBzeXN0ZW0iIHsKICBsYXllciBGUk9OVCAiQm9va2luZyBjaGFubmVscyIgewogICAgYmxvY2sgUE9SVEFMICJCb29raW5nXG5wb3J0YWwiCiAgfQogIGxheWVyIEJVU0lORVNTICJBcHBvaW50bWVudCBtYW5hZ2VtZW50IiB7CiAgICBibG9jayBTQ0hFRFVMRVIgIlNsb3Rcbm1hbmFnZW1lbnQiCiAgICBibG9jayBOT1RJRiAiTm90aWZpY2F0aW9ucyIKICB9Cn0KCmV4dGVybmFsIEVYVCAiRXh0ZXJuYWwgc3lzdGVtcyIgewogIGJsb2NrIFNNUyAiU01TIGdhdGV3YXkiCn0KCiMgLS0tLSBidXNpbmVzcyBvYmplY3RzIC0tLS0KYnVzaW5lc3Mtb2JqZWN0IEJPX0FQUFQgIkFwcG9pbnRtZW50IiAic2xvdCBib29rZWQgYnkgYSBwYXRpZW50IHdpdGggYSBwcmFjdGl0aW9uZXIiCmJ1c2luZXNzLW9iamVjdCBCT19TTE9UICJTbG90IiAidGltZSB3aW5kb3cgb3BlbiBmb3IgYm9va2luZyIKClBBVElFTlQgLT4gUE9SVEFMIDogIlNlYXJjaCBhIHNsb3RcbmFuZCBib29rIiBbQk9fU0xPVF0KUE9SVEFMIC0+IFNDSEVEVUxFUiA6ICJCb29raW5nIHJlcXVlc3QiIFtCT19BUFBUXQpTQ0hFRFVMRVIgLT4gTk9USUYgOiAiQXBwb2ludG1lbnQgY29uZmlybWVkIiBbQk9fQVBQVF0KTk9USUYgLT4gU01TIDogIlNlbmQgU01TIHJlbWluZGVyIgpTRUNSRVRBUlkgLT4gU0NIRURVTEVSIDogIk9wZW4gLyBibG9ja1xuc2xvdHMiIFtCT19TTE9UXQpTQ0hFRFVMRVIgLT4gUEFUSUVOVCA6ICJBcHBvaW50bWVudFxuY29uZmlybWF0aW9uIiBbQk9fQVBQVF0KCmxlZ2VuZCB7CiAgbm90ZSAiSGVhbHRoIGRhdGEgaXMgc3RvcmVkIG9uIGNlcnRpZmllZCBoZWFsdGgtZGF0YS1ob3N0aW5nIGluZnJhc3RydWN0dXJlIgp9Cg==)
226
+ - [Page disposition](https://cairn-psi-five.vercel.app/#src=ZGlhZ3JhbSBsb2dpY2FsICJPbmxpbmUgYXBwb2ludG1lbnQgYm9va2luZyDigJQgbG9naWNhbCB2aWV3IgpzdHlsZSB7IGRpc3Bvc2l0aW9uOiBwYWdlIH0KCmFjdG9yLWdyb3VwIFVTRVJTICJVc2VycyIgewogIGFjdG9yIFBBVElFTlQgIlBhdGllbnQiCn0KCmFjdG9yLWdyb3VwIFNUQUZGICJTdGFmZiIgewogIGFjdG9yIFNFQ1JFVEFSWSAiTWVkaWNhbCBzZWNyZXRhcnkiCn0KCnN5c3RlbSBCT09LSU5HICJBcHBvaW50bWVudCBib29raW5nIHN5c3RlbSIgewogIGxheWVyIEZST05UICJCb29raW5nIGNoYW5uZWxzIiB7CiAgICBibG9jayBQT1JUQUwgIkJvb2tpbmdcbnBvcnRhbCIKICB9CiAgbGF5ZXIgQlVTSU5FU1MgIkFwcG9pbnRtZW50IG1hbmFnZW1lbnQiIHsKICAgIGJsb2NrIFNDSEVEVUxFUiAiU2xvdFxubWFuYWdlbWVudCIKICAgIGJsb2NrIE5PVElGICJOb3RpZmljYXRpb25zIgogIH0KfQoKZXh0ZXJuYWwgRVhUICJFeHRlcm5hbCBzeXN0ZW1zIiB7CiAgYmxvY2sgU01TICJTTVMgZ2F0ZXdheSIKfQoKIyAtLS0tIGJ1c2luZXNzIG9iamVjdHMgLS0tLQpidXNpbmVzcy1vYmplY3QgQk9fQVBQVCAiQXBwb2ludG1lbnQiICJzbG90IGJvb2tlZCBieSBhIHBhdGllbnQgd2l0aCBhIHByYWN0aXRpb25lciIKYnVzaW5lc3Mtb2JqZWN0IEJPX1NMT1QgIlNsb3QiICJ0aW1lIHdpbmRvdyBvcGVuIGZvciBib29raW5nIgoKUEFUSUVOVCAtPiBQT1JUQUwgOiAiU2VhcmNoIGEgc2xvdFxuYW5kIGJvb2siIFtCT19TTE9UXQpQT1JUQUwgLT4gU0NIRURVTEVSIDogIkJvb2tpbmcgcmVxdWVzdCIgW0JPX0FQUFRdClNDSEVEVUxFUiAtPiBOT1RJRiA6ICJBcHBvaW50bWVudCBjb25maXJtZWQiIFtCT19BUFBUXQpOT1RJRiAtPiBTTVMgOiAiU2VuZCBTTVMgcmVtaW5kZXIiClNFQ1JFVEFSWSAtPiBTQ0hFRFVMRVIgOiAiT3BlbiAvIGJsb2NrXG5zbG90cyIgW0JPX1NMT1RdClNDSEVEVUxFUiAtPiBQQVRJRU5UIDogIkFwcG9pbnRtZW50XG5jb25maXJtYXRpb24iIFtCT19BUFBUXQoKbGVnZW5kIHsKICBub3RlICJIZWFsdGggZGF0YSBpcyBzdG9yZWQgb24gY2VydGlmaWVkIGhlYWx0aC1kYXRhLWhvc3RpbmcgaW5mcmFzdHJ1Y3R1cmUiCn0K)
133
227
 
134
228
 
135
229
  ### Numbered flows
@@ -160,6 +254,31 @@ Cairn comes with a collection of built-in themes. Choose the one that best fits
160
254
  </tr>
161
255
  </table>
162
256
 
257
+ Pick one from the command line with `--theme`, without editing the diagram:
258
+
259
+ ```sh
260
+ cairn build my-system.cairn --theme nord
261
+ cairn themes # list the built-in names
262
+ ```
263
+
264
+ #### Your own colours
265
+
266
+ `--theme` also takes a JSON file. It **extends a built-in and overrides only
267
+ what it names**, so a usable theme is a few lines rather than fifty colours:
268
+
269
+ ```json
270
+ {
271
+ "extends": "dark",
272
+ "dark": true,
273
+ "pal": { "bg": "#0d1117", "nStroke": "#58a6ff" },
274
+ "accentColors": { "blue": "#58a6ff", "blueF": "#0d2136" }
275
+ }
276
+ ```
277
+
278
+ ```sh
279
+ cairn build my-system.cairn --theme ./my-theme.json
280
+ ```
281
+
163
282
  ## Installation
164
283
 
165
284
  Prebuilt, self-contained binaries are published on every `v*` tag (no runtime needed). Pick your platform:
@@ -217,6 +336,7 @@ help: move this `block` inside a `layer`, `system` or `external`
217
336
 
218
337
  ```sh
219
338
  cairn build my-system.cairn -o my-system.svg # -o optional; defaults to the same name, .svg
339
+ cairn build my-system.cairn --theme nord # or --theme ./my-theme.json
220
340
  ```
221
341
 
222
342
  On validation errors nothing is written and the exit code is 1; warnings are printed but do not block.
@@ -237,6 +357,21 @@ cairn watch my-system.cairn
237
357
 
238
358
  Rebuilds the SVG on save. On a compile error the SVG becomes an error panel (codes, lines, help), so an open preview never shows a stale diagram. Watch observes only the file it was launched on — run one per file. Pair it with an editor that auto-refreshes an open SVG.
239
359
 
360
+ ### List the built-in logos
361
+
362
+ ```sh
363
+ cairn logos
364
+ ```
365
+
366
+ ```text
367
+ 37 built-in logos — use as `logo: <name>` on an element:
368
+
369
+ angular apache apachekafka apachespark django
370
+ ...
371
+ ```
372
+
373
+ Anything else: point at a file — `logo: "./logos/name.svg"`.
374
+
240
375
  ### Explain a diagnostic
241
376
 
242
377
  ```sh
@@ -248,13 +383,42 @@ E0240 — The infrastructure view requires every flow to carry its protocol (and
248
383
  relevant): the flow matrix is the primary output of this view. Add `(HTTPS/443)` after the label.
249
384
  ```
250
385
 
251
- ### Check your version
386
+ ### Check your version, and what it contains
252
387
 
253
388
  ```sh
254
389
  cairn version # or --version / -v
390
+ cairn version --licenses
255
391
  ```
256
392
 
257
- Released binaries (the ones from Homebrew, Scoop, or the GitHub Releases page) print the exact tag they were built from. Running from a clone via `npm run cairn` prints `package.json`'s version instead.
393
+ Released binaries (the ones from Homebrew, Scoop, or the GitHub Releases page) print the exact tag they were built from. Running from a clone via `npm run cairn` prints `package.json`'s version instead. The bare form prints one line and nothing else, so it stays safe to parse in a script.
394
+
395
+ `--licenses` adds the third-party notices for that exact build — elkjs under EPL-2.0, the Simple Icons artwork, and, for the released binaries only, the embedded Bun runtime and its LGPL-2.1 JavaScriptCore. It reads from inside the artifact, so it describes the copy you are running rather than whatever the repository says today.
396
+
397
+ That output is a convenience, not the licence itself. The full texts are installed with cairn: `share/doc/cairn/` for the curl installer and Homebrew, the app directory for Scoop, the package root for npm. [`THIRD-PARTY-NOTICES.md`](THIRD-PARTY-NOTICES.md) is the long form, with per-icon attribution and the LGPL relink offer.
398
+
399
+ ## License
400
+
401
+ cairn is [Apache-2.0](LICENSE). Its shipped artifacts also *contain* third-party code Apache-2.0 does not cover: elkjs (EPL-2.0) inlined into every bundle and binary, a curated set of Simple Icons paths vendored into the logos, and — in the release binaries only — the Bun runtime, which statically links JavaScriptCore under the LGPL.
402
+
403
+ Full texts live in [`licenses/`](licenses/), their provenance in [`licenses/README.md`](licenses/README.md), and the long form in [`THIRD-PARTY-NOTICES.md`](THIRD-PARTY-NOTICES.md).
404
+
405
+ ### What that means for you
406
+
407
+ Find your row. Most people are in the first one, and owe nothing.
408
+
409
+ | What you are doing | What you owe |
410
+ |---|---|
411
+ | **Sharing a diagram** | Nothing — unless it draws one of the six licensed logos below, and then cairn has already written the attribution into the SVG. Leave the comment in place. |
412
+ | **Depending on `@r0kshan/cairn`** from npm | Nothing. You distribute no copy of cairn; `npm install` fetches it with its notices intact. |
413
+ | **Bundling cairn** into your own artifact (webpack, Docker, Electron, single-file CLI) | Copy `LICENSE`, `THIRD-PARTY-NOTICES.md` and `licenses/` out of the installed package into your distribution. |
414
+ | **Redistributing a cairn binary** | The same three files — and every installer has already put them on disk beside it (`share/doc/cairn`, the app directory, or the package root, by channel). |
415
+
416
+ **Two things worth knowing:**
417
+
418
+ - **6 of the 37 built-in logos** — `angular`, `apache`, `apachekafka`, `apachespark`, `javascript`, `openjdk` — keep their own licence instead of the project-wide CC0-1.0. The other 31 ask for nothing.
419
+ - **Converting a diagram to PNG, or pasting it into a slide deck, drops the attribution comment.** The artwork is still there, so the attribution has to go somewhere you control: a caption, a credits slide, or `licenses/` shipped alongside. This is the one case cairn cannot handle for you.
420
+
421
+ Diagrams you produce are yours — cairn claims nothing in them. The brands drawn by the built-in logos remain their owners' trademarks.
258
422
 
259
423
  ## More
260
424
 
@@ -263,3 +427,4 @@ Released binaries (the ones from Homebrew, Scoop, or the GitHub Releases page) p
263
427
  - [`ARCHITECTURE.md`](documentation/ARCHITECTURE.md) — how the pipeline fits together, for contributors.
264
428
  - [`CONTRIBUTING.md`](CONTRIBUTING.md) — how to open a PR, and the gates it must pass.
265
429
  - [`documentation/`](documentation/) — architecture, invariants, DSL and internals.
430
+ - [`THIRD-PARTY-NOTICES.md`](THIRD-PARTY-NOTICES.md) — third-party licence notices, with per-icon attribution.
@@ -4,8 +4,154 @@ cairn is Apache-2.0 (`LICENSE`). Its shipped artifacts — the npm tarball's
4
4
  `bin/cairn.mjs`, the release binaries, the playground bundles — additionally
5
5
  *contain* third-party code, so this notice travels with them.
6
6
 
7
- Build-only tools (esbuild, Bun, biome, typescript) are not listed: nothing of
8
- theirs is distributed.
7
+ Build-only tools (esbuild, biome, typescript) are not listed: nothing of theirs
8
+ is distributed. Bun is a different case — `bun build --compile` embeds its
9
+ runtime in every release binary — and has its own section below.
10
+
11
+ Where each artifact carries this notice:
12
+
13
+ | Artifact | How the notices travel |
14
+ |---|---|
15
+ | npm tarball | `files` ships `LICENSE`, this file and `licenses/`; both bundles also carry a `/*!` banner naming what they inline |
16
+ | release binaries | the texts ship as a `cairn-<version>-licenses.tar.gz` release asset, unpacked into `share/doc/cairn` by `packaging/install.sh`, into the formula's `doc` by the Homebrew tap, and into the app directory by the Scoop manifest — that is what carries the licences. `install.sh` is fail-closed in its own code: it verifies the bundle against the release checksums and installs nothing at all, binary included, if it cannot. Homebrew and Scoop are given the same checksums by `scripts/render-packaging.mjs` and abort on a mismatch through their own resource verification rather than through anything cairn does. `cairn version --licenses` additionally prints the short notice from inside the binary, so a copy separated from its directory still says what it contains |
17
+ | playground | `LICENSE`, this file and `licenses/` are served from the deployed site beside the bundle and linked from the page header; the bundle carries the same banner |
18
+
19
+ ## What this means for you
20
+
21
+ The rest of this file records what cairn owes and to whom. This section is the
22
+ other direction: what, if anything, *you* take on. It is a plain reading of the
23
+ licence texts in `licenses/`, not legal advice, and the summary below is not a
24
+ substitute for the terms themselves.
25
+
26
+ Three situations, in increasing order of what they ask of you.
27
+
28
+ ### You generate diagrams with cairn
29
+
30
+ Nothing. Using a tool is not redistributing it, and every obligation in this
31
+ file attaches to distributing copies. cairn claims nothing in the diagrams you
32
+ produce.
33
+
34
+ One thing worth knowing, because it is the only case where a third party's
35
+ terms reach your output: **a rendered SVG embeds the icon artwork it draws.**
36
+ 31 of the 37 built-in logos are CC0-1.0, which waives copyright and asks for
37
+ nothing. Six carry their own terms and ask for attribution:
38
+
39
+ | Logo | Licence |
40
+ |---|---|
41
+ | `angular` | CC-BY-4.0 |
42
+ | `apache`, `apachekafka`, `apachespark` | Apache-2.0 |
43
+ | `javascript` | MIT |
44
+ | `openjdk` | BSD-3-Clause |
45
+
46
+ A diagram that uses none of those six carries no third-party artwork with
47
+ conditions attached, and cairn writes nothing extra into it. One that does is a
48
+ redistribution of that artwork, so the SVG carries its own attribution: an XML
49
+ comment after the opening `<svg>` tag naming each licensed mark it drew, the
50
+ artwork's source, the rights-holder's copyright line where one is published,
51
+ and a URL for the licence text. Only the marks actually painted are named.
52
+
53
+ That comment also records that the artwork is **modified**, because it is:
54
+ simple-icons redrew each brand mark as a single monochrome path before cairn
55
+ vendored it, and cairn paints that path in the node's colour rather than the
56
+ mark's own. CC-BY-4.0 §3(a)(1)(B) asks that a modification be indicated and
57
+ previous ones retained; Apache-2.0 §4(b) asks that a changed file say so.
58
+
59
+ **Keep that comment in the file.** It is the attribution those four licences
60
+ ask for, and an exported SVG travels without the `licenses/` directory that
61
+ discharges this everywhere else. An optimiser set to strip comments — `svgo`
62
+ does by default — removes the only notice the diagram carries. The per-icon
63
+ table further down this file says the same thing at more length, for anyone
64
+ who has the repository rather than a lone SVG.
65
+
66
+ **When the diagram stops being an SVG, the comment stops travelling.** Pasted
67
+ into a slide deck, a wiki page or a PDF, or converted to PNG, it is gone — and
68
+ that is how most diagrams reach an audience. The artwork is still there, so the
69
+ attribution still has to be somewhere: a credits slide, a caption, a colophon,
70
+ or `licenses/` shipped alongside. The per-icon table further down this file is
71
+ the text to copy; `cairn version --licenses` prints a shorter pointer to it.
72
+ This is the one case cairn cannot do for you, because nothing of cairn's is
73
+ left in the artifact by then.
74
+
75
+ One limit worth stating: Apache-2.0 §4(a) asks that recipients be given "a copy
76
+ of the License", and a comment of that size in every diagram is not practical,
77
+ so the SVG links the text rather than embedding it. CC-BY-4.0 §3(a)(3) permits
78
+ a link outright; MIT and BSD-3-Clause get the holder's copyright line inline
79
+ where one exists. If you need a diagram to carry the texts themselves, ship
80
+ `licenses/` beside it.
81
+
82
+ Separately from copyright: the brands these logos depict are their owners'
83
+ trademarks. Drawing one in a diagram is not an endorsement, and cairn grants no
84
+ rights in the marks themselves.
85
+
86
+ ### You use cairn as an npm package
87
+
88
+ Two quite different cases, and which one you are in depends on how your own
89
+ thing is distributed.
90
+
91
+ **cairn as a dependency.** Your package declares `@r0kshan/cairn` in
92
+ `dependencies` and your users' `npm install` fetches it from the registry, with
93
+ `LICENSE`, this file and `licenses/` intact in `node_modules`. You are
94
+ distributing no copies of cairn, so nothing here attaches to you. Just don't
95
+ strip those files out of what you publish.
96
+
97
+ **cairn bundled into your artifact** — a webpack/rollup/esbuild bundle, a
98
+ Docker image, an Electron app, a single-file CLI. Now cairn's code, and the
99
+ elkjs inside it, are part of what you hand someone. You take on cairn's
100
+ Apache-2.0 (`LICENSE`), elkjs' EPL-2.0 (§3.2(b) wants a copy of the Agreement
101
+ with each copy of the program), and the per-icon attribution. In practice that
102
+ is copying `LICENSE`, this file and `licenses/` out of the installed package
103
+ into your own distribution.
104
+
105
+ `bin/cairn.mjs` and `dist/cairn.mjs` each begin with a `/*!` banner naming what
106
+ they contain, and most minifiers preserve `/*!` by default. **Check your
107
+ bundler's comment settings**: one configured to strip all comments removes the
108
+ only notice the code itself carries.
109
+
110
+ Note that the icon artwork is in the bundle whether or not you ever use a
111
+ `logo:` — the table is not tree-shaken. So for redistribution the six licensed
112
+ icons above apply regardless; it is only your *output* that depends on which
113
+ logos you actually draw.
114
+
115
+ **EPL-2.0 does not reach your code.** Its copyleft covers modifications to the
116
+ EPL'd work; cairn does not modify elkjs, and bundling is not modification. Your
117
+ own code stays under whatever licence you choose. This is why cairn elects
118
+ EPL-2.0 from elkjs' dual offer rather than GPL-3.0-or-later.
119
+
120
+ ### You redistribute cairn itself
121
+
122
+ Republishing the binaries, mirroring the release assets, or shipping a cairn
123
+ executable inside something else. This is the heavy case, and it is what most
124
+ of this file exists for: the binaries additionally embed the Bun runtime, which
125
+ statically links JavaScriptCore under the LGPL in part, along with everything
126
+ else in `licenses/bun-LICENSE.md`.
127
+
128
+ The short version is that the licence texts have to travel with the binary.
129
+ Every installer cairn publishes already puts them on disk for you, but not next
130
+ to the executable: the curl installer unpacks them into `share/doc/cairn`
131
+ beside the `bin/` it installs into, Homebrew stages them into the formula's
132
+ `doc` directory, and Scoop unpacks them into the app directory. Copying just
133
+ the binary out of an install therefore leaves the notices behind — take those
134
+ files with it.
135
+
136
+ If you are building your own, this is what has to travel with it:
137
+
138
+ | What | Where it is | Why |
139
+ |---|---|---|
140
+ | `LICENSE` | repo root | cairn's own Apache-2.0, §4(a) |
141
+ | `THIRD-PARTY-NOTICES.md` | repo root | this file — the per-icon attribution and the §6 offer |
142
+ | `licenses/elkjs-EPL-2.0.md` | `licenses/` | EPL-2.0 §3.2(b), a copy with each copy |
143
+ | `licenses/simple-icons-CC0-1.0.md` and the four icon texts | `licenses/` | the six marks that carry their own terms |
144
+ | `licenses/bun-LICENSE.md` and `licenses/LGPL-2.1.txt` | `licenses/` | the runtime the binary embeds |
145
+
146
+ Shipping the whole `licenses/` directory plus those two root files is the
147
+ simplest way to get all of it, and is what every installer above does.
148
+
149
+ Two things a copied directory does not give you. The LGPL-2.1 §6 offer names a
150
+ specific WebKit revision, a specific cairn tag and a specific build script — if
151
+ you build against a different Bun, that offer is wrong and you must restate it
152
+ for what you actually linked. And `cairn version --licenses` prints from inside
153
+ the binary, so a build of your own prints your build's notice, not this one.
154
+ Read the Bun and JavaScriptCore sections below before you ship.
9
155
 
10
156
  ## elkjs
11
157
 
@@ -17,8 +163,9 @@ copy of elkjs' own `LICENSE.md`, upstream at https://www.eclipse.org/legal/epl-2
17
163
 
18
164
  That copy is vendored rather than referenced because elkjs is a *devDependency*:
19
165
  the published package installs zero dependencies, so `node_modules/elkjs/` never
20
- reaches a consumer while the inlined code does. EPL-2.0 §3.1(b) requires the
21
- license to travel with the distributed form, so `files` ships it in the tarball.
166
+ reaches a consumer while the inlined code does. The inlined form is source
167
+ code, so EPL-2.0 §3.2(b) requires a copy of the Agreement with each copy of it,
168
+ and `files` ships that copy in the tarball.
22
169
 
23
170
  Per EPL-2.0 §3.1(a): the source code for elkjs is available under the EPL-2.0,
24
171
  and can be obtained from https://github.com/kieler/elkjs or from the published
@@ -29,9 +176,166 @@ ties it back to.
29
176
  cairn asserts no ownership over elkjs and does not relicense it. Apache-2.0
30
177
  covers cairn's own code only.
31
178
 
179
+ ## Bun
180
+
181
+ The release binaries are built with `bun build --compile`
182
+ (`scripts/build-binaries.sh`), which packages cairn's code **together with the
183
+ Bun runtime** into one executable. So unlike esbuild, biome and typescript, Bun
184
+ is not merely a build tool here: part of it is distributed in every binary
185
+ cairn publishes. The npm bundles and the playground are built with esbuild and
186
+ carry none of it, and `cairn version --licenses` says so per artifact — the binaries
187
+ print the Bun paragraph, the npm CLI does not.
188
+
189
+ **Version 1.4.0.** The release workflow pins `bun-version`
190
+ (`.github/workflows/release.yml`) rather than tracking `latest`, because a
191
+ notice that cannot name the runtime it shipped is not a notice. The same pin is
192
+ recorded in `src/notice.ts` as `BUN_VERSION`.
193
+
194
+ Bun itself is MIT. Its runtime statically links a long list of further
195
+ components with their own terms. Bun's own `LICENSE.md` is the authoritative
196
+ enumeration of them, and it is reproduced verbatim at
197
+ [`licenses/bun-LICENSE.md`](./licenses/bun-LICENSE.md) — fetched from the
198
+ `bun-v1.4.0` tag, so it enumerates the runtime cairn actually embeds rather
199
+ than whatever upstream `main` says today. That file, not this section, is the
200
+ notice for everything Bun links; it is shipped with every binary.
201
+
202
+ ### JavaScriptCore and the LGPL
203
+
204
+ The component that carries real obligations is JavaScriptCore, from WebKit,
205
+ which Bun links statically and which is under the GNU Lesser (originally
206
+ Library) General Public License for part of its source. Which version takes a
207
+ sentence to state, because the sources disagree in wording rather than in
208
+ substance: Bun's `LICENSE.md` labels it `LGPL-2`, while WebKit's own file
209
+ headers offer "version 2 of the License, or (at your option) any later
210
+ version". cairn takes that offer at **2.1** — that is the text shipped at
211
+ [`licenses/LGPL-2.1.txt`](./licenses/LGPL-2.1.txt), and the §6 offer below is
212
+ written against it. tinycc, also in Bun's list, is LGPL v2.1 outright.
213
+
214
+ LGPL-2.1 §6 permits distributing a work that statically links the library
215
+ provided the recipient can modify the library and relink. cairn discharges that
216
+ as follows, and everything named here is published:
217
+
218
+ - **The library's source.** JavaScriptCore as Bun links it is at
219
+ <https://github.com/oven-sh/webkit>, at revision
220
+ `0f966e81b78c84bb23213e391bc679c4ef83e56b` — the `WEBKIT_VERSION` pinned in
221
+ `scripts/build/deps/webkit.ts` at Bun's `bun-v1.4.0` tag, which is the Bun
222
+ the release workflow is pinned to. Naming the revision rather than the
223
+ repository is the point: "the version Bun happens to use" is not something a
224
+ recipient can check, and a relink has to start from the same source the
225
+ binary was built against. Bun's `LICENSE.md` gives the relink procedure
226
+ verbatim, and it is reproduced at `licenses/bun-LICENSE.md`.
227
+ - **cairn's own source, which is the rest of the work.** Apache-2.0, at
228
+ <https://github.com/R0kshan/cairn>, at the tag the binary was built from —
229
+ `cairn version` prints that tag, and the release carries a build-provenance
230
+ attestation tying the binary to that commit.
231
+ - **The build that combines them.** `scripts/build-binaries.sh`, in the same
232
+ repository, is the whole of it: one `bun build --compile` invocation against
233
+ a pinned Bun. Anyone who relinks a modified JavaScriptCore into Bun by
234
+ upstream's procedure can rerun that script and obtain an equivalent cairn
235
+ binary.
236
+
237
+ Requests for anything in this list that you cannot obtain from those URLs
238
+ should be opened as an issue on the cairn repository.
239
+
240
+ > **This is a good-faith implementation, not a legal opinion.** Static linking
241
+ > of LGPL-2.1 code is an area where reasonable lawyers differ, particularly on
242
+ > whether an offer of source-plus-build-script is equivalent to the "object
243
+ > format" §6 speaks of. The facts above are accurate and the materials are
244
+ > genuinely published; whether they are sufficient for a given jurisdiction or
245
+ > distribution is a question for counsel. If you need certainty without that
246
+ > question, the npm and playground artifacts contain no Bun and no LGPL code at
247
+ > all.
248
+
249
+ ## simple-icons
250
+
251
+ Version 16.29.0, CC0-1.0. Upstream: https://github.com/simple-icons/simple-icons
252
+
253
+ cairn's shipped artifacts and runtime do not depend on simple-icons; only
254
+ regenerating the logos does. A curated
255
+ subset of its icon *paths* — the ones behind `logo: <name>` — is vendored into
256
+ `src/logos.ts` by `scripts/update-logos.mjs`, which fetches the pinned version
257
+ with `npm pack` and writes the paths out as source. The full license text is in
258
+ [`licenses/simple-icons-CC0-1.0.md`](./licenses/simple-icons-CC0-1.0.md), a
259
+ verbatim copy of the project's own `LICENSE.md` at that version.
260
+
261
+ That version stamp is deliberate. The file records which licence governed the
262
+ paths cairn actually ships, so it is a historical fact rather than a mirror of
263
+ upstream: a later relicence there cannot make it wrong, and cannot oblige a
264
+ cairn release. CC0 is irrevocable, so paths vendored under it stay CC0 whatever
265
+ simple-icons does next.
266
+
267
+ **The project licence is not the whole story.** Simple Icons' `DISCLAIMER.md`
268
+ says individual icons may carry their own terms, and some of the ones cairn
269
+ vendors do. `scripts/update-logos.mjs` reads each icon's declared licence and
270
+ refuses anything cairn cannot pass on under its own Apache-2.0 — so such an icon
271
+ cannot reach `src/logos.ts` even if someone adds it to the curated list. The
272
+ reasons differ by licence, and are set out with the table below.
273
+
274
+ <!-- generated by scripts/update-logos.mjs — do not edit by hand -->
275
+
276
+ Most of the 37 vendored icons carry no licence of their own and are
277
+ covered by the project-wide CC0-1.0 above. These declare their own, which
278
+ applies to that icon's artwork instead:
279
+
280
+ | Icon | Licence | Full text | Artwork source |
281
+ | --- | --- | --- | --- |
282
+ | Angular (`angular`) | CC-BY-4.0 | [`CC-BY-4.0.txt`](./licenses/CC-BY-4.0.txt) | <https://angular.dev/press-kit> |
283
+ | Apache (`apache`) | Apache-2.0 | [`Apache-2.0.txt`](./licenses/Apache-2.0.txt) | <https://www.apache.org/foundation/press/kit> |
284
+ | Apache Kafka (`apachekafka`) | Apache-2.0 | [`Apache-2.0.txt`](./licenses/Apache-2.0.txt) | <https://apache.org/logos> |
285
+ | Apache Spark (`apachespark`) | Apache-2.0 | [`Apache-2.0.txt`](./licenses/Apache-2.0.txt) | <https://apache.org/logos> |
286
+ | JavaScript (`javascript`) | MIT | [`MIT-javascript-logo.js.txt`](./licenses/MIT-javascript-logo.js.txt) | <https://github.com/voodootikigod/logo.js/blob/1544bdeed6d618a6cfe4f0650d04ab8d9cfa76d9/js.svg> |
287
+ | OpenJDK (`openjdk`) | BSD-3-Clause | [`BSD-3-Clause.txt`](./licenses/BSD-3-Clause.txt) | <https://github.com/openjdk/duke/blob/master/vector/Agent.svg> |
288
+
289
+ Each permits commercial redistribution and asks for attribution, which this
290
+ table and the shipped licence texts are. **Attribution is to the artwork
291
+ source named above.** Where a rights-holder publishes no copyright line at
292
+ that source — as is the case for several of these marks — cairn identifies
293
+ the origin by that URL rather than assert a copyright holder it cannot
294
+ verify. `licenses/BSD-3-Clause.txt` is consequently the SPDX template, with
295
+ the `<year> <owner>` fields as upstream left them; see `licenses/README.md`.
296
+
297
+ Source notes:
298
+
299
+ - **OpenJDK (`openjdk`)** — simple-icons records <https://hg.openjdk.java.net/duke/duke/file/ca00f100dafc/vector/Agent.svg>, and that Mercurial host has been retired — it answers 403. The attribution above therefore names the live GitHub location of the same file, recorded in SOURCE_OVERRIDES rather than silently swapped. Checked 2026-09-04: that repository has no LICENSE file, the GitHub API reports no licence for it, and `vector/Agent.svg` carries no copyright notice of its own. So there is no upstream copyright line to reproduce, which is why `licenses/BSD-3-Clause.txt` keeps the SPDX `<year> <owner>` fields blank and attribution for this mark is to the source URL.
300
+
301
+ Three kinds of terms are refused by the generator instead, and never reach
302
+ `src/logos.ts`, for three different reasons:
303
+
304
+ - **NonCommercial** bars the commercial use cairn's own Apache-2.0 grants
305
+ downstream, so cairn would be promising a right it does not hold.
306
+ - **ShareAlike** does permit commercial use, but requires adaptations to carry
307
+ the same licence — an obligation cairn cannot discharge on behalf of whoever
308
+ embeds the mark in their own diagram.
309
+ - **A trademark policy** in place of a licence is not a copyright grant at all,
310
+ so there is no permission to copy the artwork to rely on.
311
+
312
+ <!-- end generated -->
313
+
314
+ CC0-1.0 waives copyright and imposes no attribution requirement, so for the
315
+ icons it covers this entry is a courtesy rather than an obligation. It is kept
316
+ because the paths reach users inside every shipped artifact, and a reader
317
+ deserves to know where the artwork came from. For the icons in the table above
318
+ it is not a courtesy: MIT, BSD-3-Clause, CC-BY-4.0 and Apache-2.0 each require
319
+ the attribution it carries.
320
+
321
+ **Trademarks are a separate matter from the license.** Simple Icons' own
322
+ `DISCLAIMER.md` is explicit that CC0 covers the project while individual icons
323
+ may carry their own terms, and that brand marks remain the property of their
324
+ owners. cairn redistributes the artwork only; it claims no rights in the brands
325
+ depicted, and using a logo in a diagram does not imply the brand endorses
326
+ anything. Anyone republishing a cairn diagram is responsible for their own use
327
+ of the marks it shows.
328
+
32
329
  ---
33
330
 
34
331
  If the set of inlined dependencies changes, update this file **and** `licenses/`
35
- in the same commit — a notice that names a license text the tarball doesn't carry
36
- is the failure mode this section exists to prevent. `files` in `package.json`
37
- ships both with the npm package.
332
+ **and** `src/notice.ts` in the same commit — a notice that names a license text
333
+ the tarball doesn't carry is the failure mode this section exists to prevent.
334
+ `files` in `package.json` ships them with the npm package.
335
+
336
+ `src/notice.ts` is the single source for the short form: the bundle banners
337
+ (`scripts/notice-banner.sh`) and `cairn version --licenses` both render from it,
338
+ so they cannot drift apart. `tests/notice.test.ts` fails the build if a checked-in
339
+ artifact's banner no longer matches. The long form — full texts, per-icon
340
+ attribution, the LGPL relink offer — is this file, and `licenses/README.md`
341
+ records where each text was fetched from.