@cfbender/cesium 0.3.5

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 (44) hide show
  1. package/ARCHITECTURE.md +304 -0
  2. package/CHANGELOG.md +335 -0
  3. package/LICENSE +21 -0
  4. package/README.md +479 -0
  5. package/agents/cesium.md +39 -0
  6. package/assets/styleguide.html +857 -0
  7. package/package.json +61 -0
  8. package/src/cli/commands/ls.ts +186 -0
  9. package/src/cli/commands/open.ts +208 -0
  10. package/src/cli/commands/prune.ts +348 -0
  11. package/src/cli/commands/restart.ts +38 -0
  12. package/src/cli/commands/serve.ts +214 -0
  13. package/src/cli/commands/stop.ts +130 -0
  14. package/src/cli/commands/theme.ts +333 -0
  15. package/src/cli/index.ts +78 -0
  16. package/src/config.ts +94 -0
  17. package/src/index.ts +35 -0
  18. package/src/prompt/system-fragment.md +97 -0
  19. package/src/render/client-js.ts +316 -0
  20. package/src/render/controls.ts +302 -0
  21. package/src/render/critique.ts +360 -0
  22. package/src/render/extract.ts +83 -0
  23. package/src/render/scrub.ts +141 -0
  24. package/src/render/theme.ts +712 -0
  25. package/src/render/validate.ts +524 -0
  26. package/src/render/wrap.ts +165 -0
  27. package/src/server/api.ts +166 -0
  28. package/src/server/http.ts +195 -0
  29. package/src/server/lifecycle.ts +331 -0
  30. package/src/server/stop.ts +124 -0
  31. package/src/storage/index-cache.ts +71 -0
  32. package/src/storage/index-gen.ts +447 -0
  33. package/src/storage/lock.ts +108 -0
  34. package/src/storage/mutate.ts +396 -0
  35. package/src/storage/paths.ts +159 -0
  36. package/src/storage/project-summaries.ts +19 -0
  37. package/src/storage/theme-write.ts +19 -0
  38. package/src/storage/write.ts +75 -0
  39. package/src/tools/ask.ts +353 -0
  40. package/src/tools/critique.ts +66 -0
  41. package/src/tools/publish.ts +404 -0
  42. package/src/tools/stop.ts +53 -0
  43. package/src/tools/styleguide.ts +23 -0
  44. package/src/tools/wait.ts +192 -0
@@ -0,0 +1,857 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Cesium · Design system reference</title>
7
+ <style>
8
+ :root {
9
+ --bg: #faf9f5;
10
+ --surface: #ffffff;
11
+ --surface-2: #f0eee6;
12
+ --oat: #e3dacc;
13
+ --rule: #d1cfc5;
14
+ --ink: #141413;
15
+ --ink-soft: #3d3d3a;
16
+ --muted: #87867f;
17
+ --accent: #d97757;
18
+ --olive: #788c5d;
19
+ --code-bg: #141413;
20
+ --code-fg: #e8e6de;
21
+ --serif: ui-serif, Georgia, "Times New Roman", serif;
22
+ --sans: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
23
+ --mono: ui-monospace, "SF Mono", Menlo, Monaco, monospace;
24
+ }
25
+
26
+ /* reset */
27
+ *,
28
+ *::before,
29
+ *::after {
30
+ box-sizing: border-box;
31
+ margin: 0;
32
+ padding: 0;
33
+ }
34
+ html {
35
+ font-size: 16px;
36
+ -webkit-text-size-adjust: 100%;
37
+ }
38
+ body {
39
+ background: var(--bg);
40
+ color: var(--ink);
41
+ font-family: var(--sans);
42
+ font-size: 1rem;
43
+ line-height: 1.6;
44
+ padding: clamp(20px, 4vw, 56px);
45
+ }
46
+ a {
47
+ color: var(--accent);
48
+ text-decoration: underline;
49
+ }
50
+ a:hover {
51
+ opacity: 0.8;
52
+ }
53
+ img,
54
+ svg {
55
+ max-width: 100%;
56
+ height: auto;
57
+ }
58
+ p {
59
+ margin-bottom: 1em;
60
+ }
61
+ ul,
62
+ ol {
63
+ padding-left: 1.5em;
64
+ margin-bottom: 1em;
65
+ }
66
+
67
+ /* layout */
68
+ .page {
69
+ max-width: 1120px;
70
+ margin: 0 auto;
71
+ }
72
+ section {
73
+ margin-bottom: 64px;
74
+ }
75
+
76
+ /* typography */
77
+ h1,
78
+ h2,
79
+ h3,
80
+ h4,
81
+ h5,
82
+ h6 {
83
+ font-family: var(--serif);
84
+ color: var(--ink);
85
+ line-height: 1.2;
86
+ margin-bottom: 0.5em;
87
+ }
88
+
89
+ /* eyebrow */
90
+ .eyebrow {
91
+ font-family: var(--mono);
92
+ font-size: 0.7rem;
93
+ font-weight: 600;
94
+ letter-spacing: 0.1em;
95
+ text-transform: uppercase;
96
+ color: var(--muted);
97
+ margin-bottom: 0.5em;
98
+ }
99
+
100
+ /* headings */
101
+ .h-display {
102
+ font-family: var(--serif);
103
+ font-size: clamp(2rem, 5vw, 3.25rem);
104
+ font-weight: 700;
105
+ line-height: 1.1;
106
+ color: var(--ink);
107
+ margin-bottom: 0.75em;
108
+ }
109
+ .h-section {
110
+ font-family: var(--serif);
111
+ font-size: 1.5rem;
112
+ font-weight: 600;
113
+ color: var(--ink);
114
+ margin-bottom: 0.5em;
115
+ }
116
+ .section-num {
117
+ display: inline-flex;
118
+ align-items: center;
119
+ justify-content: center;
120
+ background: var(--oat);
121
+ color: var(--ink-soft);
122
+ font-family: var(--mono);
123
+ font-size: 0.75rem;
124
+ font-weight: 700;
125
+ border-radius: 6px;
126
+ padding: 2px 7px;
127
+ margin-right: 0.5em;
128
+ vertical-align: middle;
129
+ }
130
+
131
+ /* card */
132
+ .card {
133
+ background: var(--surface);
134
+ border: 1.5px solid var(--rule);
135
+ border-radius: 12px;
136
+ padding: 18px 22px;
137
+ margin-bottom: 1.5em;
138
+ }
139
+
140
+ /* tldr */
141
+ .tldr {
142
+ background: var(--surface);
143
+ border-left: 4px solid var(--accent);
144
+ border-radius: 0 12px 12px 0;
145
+ padding: 16px 20px;
146
+ margin-bottom: 1.5em;
147
+ font-size: 1.05rem;
148
+ color: var(--ink-soft);
149
+ }
150
+
151
+ /* callout */
152
+ .callout {
153
+ border-radius: 8px;
154
+ padding: 14px 18px;
155
+ margin-bottom: 1.25em;
156
+ border: 1.5px solid var(--rule);
157
+ background: var(--surface-2);
158
+ color: var(--ink-soft);
159
+ font-size: 0.95rem;
160
+ }
161
+ .callout.note {
162
+ border-color: var(--olive);
163
+ background: color-mix(in srgb, var(--olive) 10%, var(--surface));
164
+ }
165
+ .callout.warn {
166
+ border-color: var(--accent);
167
+ background: color-mix(in srgb, var(--accent) 10%, var(--surface));
168
+ }
169
+ .callout.risk {
170
+ border-color: #b45309;
171
+ background: color-mix(in srgb, #b45309 10%, var(--surface));
172
+ }
173
+
174
+ /* code */
175
+ .code {
176
+ background: var(--code-bg);
177
+ color: var(--code-fg);
178
+ font-family: var(--mono);
179
+ font-size: 0.875rem;
180
+ line-height: 1.6;
181
+ border-radius: 8px;
182
+ padding: 16px 20px;
183
+ overflow-x: auto;
184
+ margin-bottom: 1.25em;
185
+ white-space: pre;
186
+ }
187
+ .code .kw {
188
+ color: var(--accent);
189
+ }
190
+ .code .str {
191
+ color: var(--olive);
192
+ }
193
+ .code .cm {
194
+ color: var(--muted);
195
+ font-style: italic;
196
+ }
197
+ .code .fn {
198
+ color: #d4a85a;
199
+ }
200
+
201
+ /* timeline */
202
+ .timeline {
203
+ list-style: none;
204
+ padding: 0;
205
+ position: relative;
206
+ }
207
+ .timeline::before {
208
+ content: "";
209
+ position: absolute;
210
+ left: 9px;
211
+ top: 6px;
212
+ bottom: 6px;
213
+ width: 2px;
214
+ background: var(--rule);
215
+ }
216
+ .timeline li {
217
+ position: relative;
218
+ padding-left: 32px;
219
+ margin-bottom: 1.25em;
220
+ }
221
+ .timeline li::before {
222
+ content: "";
223
+ position: absolute;
224
+ left: 2px;
225
+ top: 6px;
226
+ width: 14px;
227
+ height: 14px;
228
+ border-radius: 50%;
229
+ background: var(--oat);
230
+ border: 2px solid var(--accent);
231
+ }
232
+
233
+ /* diagram */
234
+ .diagram {
235
+ border: 1.5px solid var(--rule);
236
+ border-radius: 12px;
237
+ padding: 20px;
238
+ text-align: center;
239
+ margin-bottom: 1.5em;
240
+ background: var(--surface);
241
+ }
242
+ .diagram figcaption {
243
+ font-size: 0.85rem;
244
+ color: var(--muted);
245
+ margin-top: 10px;
246
+ font-family: var(--sans);
247
+ }
248
+
249
+ /* compare-table */
250
+ .compare-table {
251
+ width: 100%;
252
+ border-collapse: collapse;
253
+ margin-bottom: 1.5em;
254
+ font-size: 0.95rem;
255
+ }
256
+ .compare-table th,
257
+ .compare-table td {
258
+ border: 1.5px solid var(--rule);
259
+ padding: 10px 14px;
260
+ text-align: left;
261
+ vertical-align: top;
262
+ }
263
+ .compare-table th {
264
+ background: var(--surface-2);
265
+ font-family: var(--sans);
266
+ font-weight: 600;
267
+ color: var(--ink);
268
+ }
269
+ .compare-table tr:nth-child(even) td {
270
+ background: var(--surface-2);
271
+ }
272
+
273
+ /* risk-table */
274
+ .risk-table {
275
+ width: 100%;
276
+ border-collapse: collapse;
277
+ margin-bottom: 1.5em;
278
+ font-size: 0.95rem;
279
+ }
280
+ .risk-table th,
281
+ .risk-table td {
282
+ border: 1.5px solid var(--rule);
283
+ padding: 10px 14px;
284
+ text-align: left;
285
+ vertical-align: top;
286
+ }
287
+ .risk-table th {
288
+ background: var(--surface-2);
289
+ font-family: var(--sans);
290
+ font-weight: 600;
291
+ }
292
+ .risk-table td:first-child {
293
+ font-weight: 600;
294
+ color: var(--ink-soft);
295
+ }
296
+
297
+ /* inline chips */
298
+ .kbd {
299
+ display: inline-block;
300
+ font-family: var(--mono);
301
+ font-size: 0.8em;
302
+ background: var(--surface-2);
303
+ border: 1.5px solid var(--rule);
304
+ border-radius: 4px;
305
+ padding: 1px 6px;
306
+ color: var(--ink-soft);
307
+ white-space: nowrap;
308
+ }
309
+ .pill {
310
+ display: inline-block;
311
+ font-family: var(--sans);
312
+ font-size: 0.8em;
313
+ font-weight: 500;
314
+ background: var(--oat);
315
+ border-radius: 20px;
316
+ padding: 2px 10px;
317
+ color: var(--ink-soft);
318
+ white-space: nowrap;
319
+ }
320
+ .tag {
321
+ display: inline-block;
322
+ font-family: var(--mono);
323
+ font-size: 0.75em;
324
+ font-weight: 600;
325
+ background: var(--surface-2);
326
+ border: 1px solid var(--rule);
327
+ border-radius: 6px;
328
+ padding: 2px 8px;
329
+ color: var(--muted);
330
+ text-transform: lowercase;
331
+ }
332
+
333
+ /* byline */
334
+ .byline {
335
+ border-top: 1.5px solid var(--rule);
336
+ margin-top: 64px;
337
+ padding-top: 18px;
338
+ font-family: var(--mono);
339
+ font-size: 0.75rem;
340
+ color: var(--muted);
341
+ display: flex;
342
+ flex-wrap: wrap;
343
+ gap: 1em;
344
+ align-items: center;
345
+ }
346
+ .byline a {
347
+ color: var(--muted);
348
+ }
349
+ </style>
350
+ </head>
351
+ <body>
352
+ <div class="page">
353
+ <!-- ============================================================
354
+ Section 1 · Header
355
+ ============================================================ -->
356
+ <section>
357
+ <p class="eyebrow">Reference · cesium design system</p>
358
+ <h1 class="h-display">Design system</h1>
359
+ <div class="tldr">
360
+ <p>
361
+ Cesium provides a small vocabulary of HTML classes for producing beautiful,
362
+ self-contained artifact pages — plans, reviews, comparisons, reports, and explainers.
363
+ Every artifact is a single HTML file that requires no external resources: fonts, colors,
364
+ and layout are fully inlined. This reference page demonstrates every available class so
365
+ an agent can internalize the design language before writing a complex artifact.
366
+ </p>
367
+ </div>
368
+ </section>
369
+
370
+ <!-- ============================================================
371
+ Section 2 · Typography &amp; Headings
372
+ ============================================================ -->
373
+ <section>
374
+ <p class="eyebrow">02 · typography</p>
375
+ <h2 class="h-section"><span class="section-num">02</span>Typography &amp; Headings</h2>
376
+ <p>
377
+ Use when you need to establish visual hierarchy. The display heading anchors the page;
378
+ section headings break the document into scannable chunks.
379
+ </p>
380
+
381
+ <h1 class="h-display">Display heading — the page title</h1>
382
+ <h2 class="h-section">Section heading — a major division</h2>
383
+ <p>
384
+ Body copy is set in the system sans-serif at 1rem/1.6. Use sparingly; prefer short
385
+ paragraphs with plenty of whitespace. Inline <code>code</code> renders in the monospace
386
+ stack at 0.9em.
387
+ </p>
388
+ <p>
389
+ Long-form explanatory text sits comfortably at this measure. Keep paragraphs to 3–5
390
+ sentences so readers can skim the first sentence of each block.
391
+ </p>
392
+ </section>
393
+
394
+ <!-- ============================================================
395
+ Section 3 · Eyebrow + Section numbering
396
+ ============================================================ -->
397
+ <section>
398
+ <p class="eyebrow">03 · eyebrow &amp; section-num</p>
399
+ <h2 class="h-section"><span class="section-num">03</span>Eyebrow + Section numbering</h2>
400
+ <p>
401
+ Use an <code>.eyebrow</code> above a section head to provide a micro-label — a category, a
402
+ step number, or a module name. Pair it with <code>.section-num</code> chips inside the
403
+ heading itself when you want the number visually attached to the title.
404
+ </p>
405
+
406
+ <div style="border-left: 3px solid var(--rule); padding-left: 1.25em; margin-bottom: 1em">
407
+ <p class="eyebrow">phase one · discovery</p>
408
+ <h2 class="h-section"><span class="section-num">01</span>Stakeholder interviews</h2>
409
+ <p>Map the landscape before writing a line of code.</p>
410
+ </div>
411
+
412
+ <div style="border-left: 3px solid var(--rule); padding-left: 1.25em">
413
+ <p class="eyebrow">phase two · design</p>
414
+ <h2 class="h-section"><span class="section-num">02</span>Information architecture</h2>
415
+ <p>Derive the navigation structure from task frequency, not org-chart hierarchy.</p>
416
+ </div>
417
+ </section>
418
+
419
+ <!-- ============================================================
420
+ Section 4 · Cards
421
+ ============================================================ -->
422
+ <section>
423
+ <p class="eyebrow">04 · cards</p>
424
+ <h2 class="h-section"><span class="section-num">04</span>Cards</h2>
425
+ <p>
426
+ Use <code>.card</code> for any discrete, bordered surface — a finding, a recommendation, a
427
+ team member bio, or a feature description. Cards sit in a CSS grid for multi-column
428
+ layouts.
429
+ </p>
430
+
431
+ <div
432
+ style="
433
+ display: grid;
434
+ grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
435
+ gap: 1rem;
436
+ "
437
+ >
438
+ <div class="card">
439
+ <p class="eyebrow">finding 01</p>
440
+ <h3 style="font-family: var(--serif); margin-bottom: 0.4em">
441
+ Session timeout too short
442
+ </h3>
443
+ <p style="margin: 0">
444
+ Users are logged out mid-task. Extend the idle timeout from 15 to 30 minutes and add a
445
+ one-minute warning banner.
446
+ </p>
447
+ </div>
448
+ <div class="card">
449
+ <p class="eyebrow">finding 02</p>
450
+ <h3 style="font-family: var(--serif); margin-bottom: 0.4em">No mobile breakpoint</h3>
451
+ <p style="margin: 0">
452
+ The dashboard overflows on viewports narrower than 768 px. A responsive grid reflow
453
+ fixes the majority of complaints.
454
+ </p>
455
+ </div>
456
+ <div class="card">
457
+ <p class="eyebrow">finding 03</p>
458
+ <h3 style="font-family: var(--serif); margin-bottom: 0.4em">
459
+ Search latency p95 = 4.2 s
460
+ </h3>
461
+ <p style="margin: 0">
462
+ Full-text search scans the entire corpus on every keystroke. Add a debounce of 300 ms
463
+ and an n-gram index to bring p95 under 400 ms.
464
+ </p>
465
+ </div>
466
+ </div>
467
+ </section>
468
+
469
+ <!-- ============================================================
470
+ Section 5 · TLDR
471
+ ============================================================ -->
472
+ <section>
473
+ <p class="eyebrow">05 · tldr</p>
474
+ <h2 class="h-section"><span class="section-num">05</span>TLDR</h2>
475
+ <p>
476
+ Use exactly <strong>one</strong> <code>.tldr</code> block per document, positioned near
477
+ the top. It is the executive summary — three to five sentences that give a busy reader
478
+ everything they need without reading the rest of the document.
479
+ </p>
480
+
481
+ <div class="tldr">
482
+ <p>
483
+ The authentication refactor is ready to ship. We replaced the legacy session cookie
484
+ approach with short-lived JWTs (15 min access, 7 day refresh) backed by a Redis token
485
+ store. All 142 existing auth tests pass; three new integration tests cover the refresh
486
+ flow. The breaking change is limited to the <code>/api/v1/auth</code> namespace — no
487
+ front-end changes required.
488
+ </p>
489
+ </div>
490
+ </section>
491
+
492
+ <!-- ============================================================
493
+ Section 6 · Callouts
494
+ ============================================================ -->
495
+ <section>
496
+ <p class="eyebrow">06 · callouts</p>
497
+ <h2 class="h-section"><span class="section-num">06</span>Callouts</h2>
498
+ <p>
499
+ Use callouts to surface information that needs to be noticed but doesn't belong in running
500
+ prose. Three flavors: <code>.callout.note</code> for helpful context,
501
+ <code>.callout.warn</code> for things that can go wrong, and
502
+ <code>.callout.risk</code> for blocking or high-severity concerns.
503
+ </p>
504
+
505
+ <div class="callout note">
506
+ <strong>Note:</strong> The refresh token rotation strategy requires a Redis instance with
507
+ persistence enabled — an in-memory-only instance will cause silent logouts on server
508
+ restart.
509
+ </div>
510
+ <div class="callout warn">
511
+ <strong>Warning:</strong> Deploying this change to production before migrating the
512
+ existing session cookies will log out all active users simultaneously.
513
+ </div>
514
+ <div class="callout risk">
515
+ <strong>Risk:</strong> The token revocation list is stored only in Redis; a cache flush
516
+ between access-token expiry and refresh will allow briefly-revoked tokens to remain valid.
517
+ </div>
518
+ </section>
519
+
520
+ <!-- ============================================================
521
+ Section 7 · Code blocks
522
+ ============================================================ -->
523
+ <section>
524
+ <p class="eyebrow">07 · code</p>
525
+ <h2 class="h-section"><span class="section-num">07</span>Code blocks</h2>
526
+ <p>
527
+ Use <code>.code</code> for multi-line code samples. Highlight tokens with nested spans:
528
+ <code>.kw</code> for keywords, <code>.str</code> for string literals, <code>.cm</code> for
529
+ comments, and <code>.fn</code> for function names. Inline <code>code</code> elements in
530
+ body copy use the browser default monospace style.
531
+ </p>
532
+
533
+ <div class="code">
534
+ <span class="cm">// Refresh an access token using the stored refresh token.</span>
535
+ <span class="kw">export</span> <span class="kw">async function</span>
536
+ <span class="fn">refreshAccessToken</span>( refreshToken: <span class="kw">string</span>,
537
+ ): <span class="kw">Promise</span>&lt;{ accessToken: <span class="kw">string</span> }&gt;
538
+ { <span class="kw">const</span> payload = <span class="kw">await</span>
539
+ <span class="fn">verifyRefreshToken</span>(refreshToken);
540
+ <span class="kw">if</span> (!payload) { <span class="kw">throw new</span>
541
+ <span class="fn">Error</span>(<span class="str">"invalid or expired refresh token"</span
542
+ >); } <span class="kw">const</span> accessToken = <span class="kw">await</span>
543
+ <span class="fn">signAccessToken</span>({ sub: payload.sub, scope: payload.scope, });
544
+ <span class="kw">return</span> { accessToken }; }
545
+ </div>
546
+
547
+ <p>
548
+ Inline usage: press <code>⌘ S</code> to save, or call
549
+ <code>refreshAccessToken(token)</code> directly from the REPL.
550
+ </p>
551
+ </section>
552
+
553
+ <!-- ============================================================
554
+ Section 8 · Timeline
555
+ ============================================================ -->
556
+ <section>
557
+ <p class="eyebrow">08 · timeline</p>
558
+ <h2 class="h-section"><span class="section-num">08</span>Timeline</h2>
559
+ <p>
560
+ Use <code>.timeline</code> for ordered milestones, release schedules, or onboarding
561
+ sequences. Each <code>&lt;li&gt;</code> gets a dot and a vertical connector automatically
562
+ via CSS.
563
+ </p>
564
+
565
+ <ul class="timeline">
566
+ <li>
567
+ <strong>Week 1 — Audit &amp; planning</strong>
568
+ <p style="margin: 0.25em 0 0">
569
+ Review existing auth code, identify all call sites, write migration checklist.
570
+ Deliverable: annotated codebase + go/no-go criteria.
571
+ </p>
572
+ </li>
573
+ <li>
574
+ <strong>Week 2–3 — Implementation</strong>
575
+ <p style="margin: 0.25em 0 0">
576
+ Build JWT signing, Redis token store, refresh rotation, and revocation list.
577
+ Feature-flagged behind <code>AUTH_V2</code>.
578
+ </p>
579
+ </li>
580
+ <li>
581
+ <strong>Week 4 — Hardening &amp; rollout</strong>
582
+ <p style="margin: 0.25em 0 0">
583
+ Load test at 3× peak traffic, canary 10% of prod, monitor error rates for 48 h, then
584
+ full cutover.
585
+ </p>
586
+ </li>
587
+ </ul>
588
+ </section>
589
+
590
+ <!-- ============================================================
591
+ Section 9 · Diagram
592
+ ============================================================ -->
593
+ <section>
594
+ <p class="eyebrow">09 · diagram</p>
595
+ <h2 class="h-section"><span class="section-num">09</span>Diagram</h2>
596
+ <p>
597
+ Use <code>.diagram</code> wrapping an inline <code>&lt;svg&gt;</code> for architecture
598
+ diagrams, flow charts, and data-flow illustrations. Include a
599
+ <code>&lt;figcaption&gt;</code> below the SVG for a brief description.
600
+ </p>
601
+
602
+ <figure class="diagram">
603
+ <svg
604
+ viewBox="0 0 480 120"
605
+ width="480"
606
+ height="120"
607
+ xmlns="http://www.w3.org/2000/svg"
608
+ style="font-family: ui-monospace, monospace; max-width: 100%"
609
+ >
610
+ <!-- Client box -->
611
+ <rect
612
+ x="10"
613
+ y="35"
614
+ width="110"
615
+ height="50"
616
+ rx="8"
617
+ ry="8"
618
+ fill="#FFFFFF"
619
+ stroke="#D1CFC5"
620
+ stroke-width="1.5"
621
+ />
622
+ <text
623
+ x="65"
624
+ y="57"
625
+ text-anchor="middle"
626
+ font-size="11"
627
+ fill="#141413"
628
+ font-weight="600"
629
+ >
630
+ Client
631
+ </text>
632
+ <text x="65" y="72" text-anchor="middle" font-size="9" fill="#87867F">
633
+ browser / app
634
+ </text>
635
+
636
+ <!-- Arrow 1 -->
637
+ <line
638
+ x1="120"
639
+ y1="60"
640
+ x2="178"
641
+ y2="60"
642
+ stroke="#D1CFC5"
643
+ stroke-width="1.5"
644
+ marker-end="url(#arrow)"
645
+ />
646
+ <text x="149" y="54" text-anchor="middle" font-size="9" fill="#87867F">JWT</text>
647
+
648
+ <!-- API Gateway box -->
649
+ <rect
650
+ x="180"
651
+ y="35"
652
+ width="120"
653
+ height="50"
654
+ rx="8"
655
+ ry="8"
656
+ fill="#FFFFFF"
657
+ stroke="#D97757"
658
+ stroke-width="1.5"
659
+ />
660
+ <text
661
+ x="240"
662
+ y="57"
663
+ text-anchor="middle"
664
+ font-size="11"
665
+ fill="#141413"
666
+ font-weight="600"
667
+ >
668
+ API Gateway
669
+ </text>
670
+ <text x="240" y="72" text-anchor="middle" font-size="9" fill="#87867F">
671
+ verify + route
672
+ </text>
673
+
674
+ <!-- Arrow 2 -->
675
+ <line
676
+ x1="300"
677
+ y1="60"
678
+ x2="358"
679
+ y2="60"
680
+ stroke="#D1CFC5"
681
+ stroke-width="1.5"
682
+ marker-end="url(#arrow)"
683
+ />
684
+ <text x="329" y="54" text-anchor="middle" font-size="9" fill="#87867F">claims</text>
685
+
686
+ <!-- Service box -->
687
+ <rect
688
+ x="360"
689
+ y="35"
690
+ width="110"
691
+ height="50"
692
+ rx="8"
693
+ ry="8"
694
+ fill="#FFFFFF"
695
+ stroke="#D1CFC5"
696
+ stroke-width="1.5"
697
+ />
698
+ <text
699
+ x="415"
700
+ y="57"
701
+ text-anchor="middle"
702
+ font-size="11"
703
+ fill="#141413"
704
+ font-weight="600"
705
+ >
706
+ Service
707
+ </text>
708
+ <text x="415" y="72" text-anchor="middle" font-size="9" fill="#87867F">
709
+ business logic
710
+ </text>
711
+
712
+ <!-- Arrowhead marker -->
713
+ <defs>
714
+ <marker id="arrow" markerWidth="8" markerHeight="8" refX="6" refY="3" orient="auto">
715
+ <path d="M0,0 L0,6 L8,3 z" fill="#D1CFC5" />
716
+ </marker>
717
+ </defs>
718
+ </svg>
719
+ <figcaption>
720
+ JWT flows from the client through the API Gateway (which verifies the signature and
721
+ extracts claims) to the downstream service.
722
+ </figcaption>
723
+ </figure>
724
+ </section>
725
+
726
+ <!-- ============================================================
727
+ Section 10 · Compare table
728
+ ============================================================ -->
729
+ <section>
730
+ <p class="eyebrow">10 · compare-table</p>
731
+ <h2 class="h-section"><span class="section-num">10</span>Compare table</h2>
732
+ <p>
733
+ Use <code>.compare-table</code> when presenting a decision matrix or feature comparison
734
+ across two or more options. Keep columns to three or fewer for readability on narrow
735
+ viewports.
736
+ </p>
737
+
738
+ <table class="compare-table">
739
+ <thead>
740
+ <tr>
741
+ <th>Approach</th>
742
+ <th>Complexity</th>
743
+ <th>Operational cost</th>
744
+ </tr>
745
+ </thead>
746
+ <tbody>
747
+ <tr>
748
+ <td>Session cookies + DB</td>
749
+ <td>Low — well-understood pattern, many libraries</td>
750
+ <td>Requires sticky sessions or a shared session store</td>
751
+ </tr>
752
+ <tr>
753
+ <td>JWT (stateless)</td>
754
+ <td>Medium — signing, rotation, and revocation add surface area</td>
755
+ <td>No server-side state; tokens carry their own claims</td>
756
+ </tr>
757
+ <tr>
758
+ <td>JWT + Redis revocation</td>
759
+ <td>Medium-high — Redis dependency, invalidation logic</td>
760
+ <td>Adds Redis infra; enables immediate revocation unlike pure JWT</td>
761
+ </tr>
762
+ </tbody>
763
+ </table>
764
+ </section>
765
+
766
+ <!-- ============================================================
767
+ Section 11 · Risk table
768
+ ============================================================ -->
769
+ <section>
770
+ <p class="eyebrow">11 · risk-table</p>
771
+ <h2 class="h-section"><span class="section-num">11</span>Risk table</h2>
772
+ <p>
773
+ Use <code>.risk-table</code> for structured risk registers — likelihood, impact, and
774
+ mitigation. Use <code>.pill</code> chips inside cells to tag severity levels at a glance.
775
+ </p>
776
+
777
+ <table class="risk-table">
778
+ <thead>
779
+ <tr>
780
+ <th>Risk</th>
781
+ <th>Likelihood · Impact</th>
782
+ <th>Mitigation</th>
783
+ </tr>
784
+ </thead>
785
+ <tbody>
786
+ <tr>
787
+ <td>Redis unavailable at token refresh time</td>
788
+ <td><span class="pill">Medium</span> · <span class="pill">High</span></td>
789
+ <td>
790
+ Circuit-break to read-only mode; allow access tokens to coast until Redis recovers
791
+ (max 15 min gap).
792
+ </td>
793
+ </tr>
794
+ <tr>
795
+ <td>Private key leaked in environment variable</td>
796
+ <td><span class="pill">Low</span> · <span class="pill">Critical</span></td>
797
+ <td>
798
+ Store key in a secrets manager (Vault / AWS Secrets Manager); rotate on any
799
+ suspected exposure; never log the key.
800
+ </td>
801
+ </tr>
802
+ <tr>
803
+ <td>Clock skew causes spurious token rejection</td>
804
+ <td><span class="pill">Medium</span> · <span class="pill">Medium</span></td>
805
+ <td>
806
+ Allow a 30-second leeway window in the JWT library; add NTP monitoring to all nodes.
807
+ </td>
808
+ </tr>
809
+ </tbody>
810
+ </table>
811
+ </section>
812
+
813
+ <!-- ============================================================
814
+ Section 12 · Inline chips
815
+ ============================================================ -->
816
+ <section>
817
+ <p class="eyebrow">12 · inline chips</p>
818
+ <h2 class="h-section"><span class="section-num">12</span>Inline chips</h2>
819
+ <p>
820
+ Three chip types for inline metadata and labeling. Use them sparingly so they retain
821
+ visual contrast against body copy.
822
+ </p>
823
+
824
+ <div class="card">
825
+ <p style="margin-bottom: 0.75em">
826
+ <strong>.kbd</strong> — keyboard shortcuts and command tokens:<br />
827
+ Press <span class="kbd">⌘K</span> to open the command palette,
828
+ <span class="kbd">⌘⇧P</span> for the command runner, or <span class="kbd">Esc</span> to
829
+ dismiss any modal.
830
+ </p>
831
+ <p style="margin-bottom: 0.75em">
832
+ <strong>.pill</strong> — rounded status or severity labels:<br />
833
+ <span class="pill">v2.4.1</span>
834
+ <span class="pill">stable</span>
835
+ <span class="pill">High</span>
836
+ <span class="pill">In progress</span>
837
+ </p>
838
+ <p style="margin: 0">
839
+ <strong>.tag</strong> — lowercase monospace category tags:<br />
840
+ <span class="tag">auth</span>
841
+ <span class="tag">security</span>
842
+ <span class="tag">breaking-change</span>
843
+ <span class="tag">performance</span>
844
+ </p>
845
+ </div>
846
+ </section>
847
+
848
+ <!-- ============================================================
849
+ Footer / byline
850
+ ============================================================ -->
851
+ <footer class="byline">
852
+ <span>cesium design system reference</span>
853
+ <span>version: 0.0.0</span>
854
+ </footer>
855
+ </div>
856
+ </body>
857
+ </html>