@karmaniverous/jeeves-server 3.1.2 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/auth/keys.ts CHANGED
@@ -44,12 +44,29 @@ function pathMatchesPatterns(requestPath: string, patterns: string[]): boolean {
44
44
 
45
45
  /**
46
46
  * Check whether a request path matches normalized scopes (allow/deny).
47
- * Path must match at least one allow rule AND NOT match any deny rule.
47
+ *
48
+ * Evaluation order (explicit overrides take highest precedence):
49
+ * 1. If explicitDeny matches → DENIED (overrides named allow)
50
+ * 2. If explicitAllow matches → ALLOWED (overrides named deny)
51
+ * 3. Path must match at least one allow rule AND NOT match any deny rule.
48
52
  */
49
53
  function pathMatchesScopes(
50
54
  requestPath: string,
51
55
  scopes: NormalizedScopes,
52
56
  ): boolean {
57
+ // Explicit overrides take precedence over named scope patterns
58
+ if (
59
+ scopes.explicitDeny.length > 0 &&
60
+ pathMatchesPatterns(requestPath, scopes.explicitDeny)
61
+ )
62
+ return false;
63
+ if (
64
+ scopes.explicitAllow.length > 0 &&
65
+ pathMatchesPatterns(requestPath, scopes.explicitAllow)
66
+ )
67
+ return true;
68
+
69
+ // Standard evaluation: allow AND NOT deny
53
70
  if (!pathMatchesPatterns(requestPath, scopes.allow)) return false;
54
71
  if (scopes.deny.length > 0 && pathMatchesPatterns(requestPath, scopes.deny))
55
72
  return false;
@@ -147,7 +147,12 @@ describe('loadConfig', () => {
147
147
  const config = await loadConfig(configPath);
148
148
  expect(
149
149
  config.resolvedInsiders.find((i) => i.email === 'a@example.com')?.scopes,
150
- ).toEqual({ allow: ['/docs/**'], deny: [] });
150
+ ).toEqual({
151
+ allow: ['/docs/**'],
152
+ deny: [],
153
+ explicitAllow: [],
154
+ explicitDeny: [],
155
+ });
151
156
  });
152
157
  });
153
158
 
@@ -25,13 +25,20 @@ describe('normalizeScopes', () => {
25
25
  });
26
26
 
27
27
  it('wraps a string in allow array', () => {
28
- expect(normalizeScopes('/docs')).toEqual({ allow: ['/docs'], deny: [] });
28
+ expect(normalizeScopes('/docs')).toEqual({
29
+ allow: ['/docs'],
30
+ deny: [],
31
+ explicitAllow: [],
32
+ explicitDeny: [],
33
+ });
29
34
  });
30
35
 
31
36
  it('wraps an array as allow', () => {
32
37
  expect(normalizeScopes(['/a', '/b'])).toEqual({
33
38
  allow: ['/a', '/b'],
34
39
  deny: [],
40
+ explicitAllow: [],
41
+ explicitDeny: [],
35
42
  });
36
43
  });
37
44
 
@@ -39,12 +46,18 @@ describe('normalizeScopes', () => {
39
46
  expect(normalizeScopes({ deny: ['/secret'] })).toEqual({
40
47
  allow: ['/**'],
41
48
  deny: ['/secret'],
49
+ explicitAllow: [],
50
+ explicitDeny: [],
42
51
  });
43
52
  });
44
53
 
45
54
  it('passes through complete object', () => {
46
55
  const scopes = { allow: ['/a'], deny: ['/b'] };
47
- expect(normalizeScopes(scopes)).toEqual(scopes);
56
+ expect(normalizeScopes(scopes)).toEqual({
57
+ ...scopes,
58
+ explicitAllow: [],
59
+ explicitDeny: [],
60
+ });
48
61
  });
49
62
  });
50
63
 
@@ -65,7 +78,12 @@ describe('resolveKeys', () => {
65
78
  );
66
79
  expect(result[0].name).toBe('scoped');
67
80
  expect(result[0].seed).toBe('seed456');
68
- expect(result[0].scopes).toEqual({ allow: ['/docs'], deny: [] });
81
+ expect(result[0].scopes).toEqual({
82
+ allow: ['/docs'],
83
+ deny: [],
84
+ explicitAllow: [],
85
+ explicitDeny: [],
86
+ });
69
87
  });
70
88
 
71
89
  it('handles mixed entries', () => {
@@ -78,7 +96,12 @@ describe('resolveKeys', () => {
78
96
  );
79
97
  expect(result).toHaveLength(2);
80
98
  expect(result[0].scopes).toBe(null);
81
- expect(result[1].scopes).toEqual({ allow: ['/x'], deny: ['/y'] });
99
+ expect(result[1].scopes).toEqual({
100
+ allow: ['/x'],
101
+ deny: ['/y'],
102
+ explicitAllow: [],
103
+ explicitDeny: [],
104
+ });
82
105
  });
83
106
  });
84
107
 
@@ -128,6 +151,8 @@ describe('resolveNamedScopes', () => {
128
151
  expect(resolveNamedScopes(named, 'restricted')).toEqual({
129
152
  allow: ['/**'],
130
153
  deny: ['/secret'],
154
+ explicitAllow: [],
155
+ explicitDeny: [],
131
156
  });
132
157
  });
133
158
 
@@ -144,6 +169,8 @@ describe('resolveNamedScopes', () => {
144
169
  ).toEqual({
145
170
  allow: ['/**', '/extra'],
146
171
  deny: ['/secret', '/vc/**', '/more'],
172
+ explicitAllow: ['/extra'],
173
+ explicitDeny: ['/more'],
147
174
  });
148
175
  });
149
176
 
@@ -152,6 +179,8 @@ describe('resolveNamedScopes', () => {
152
179
  expect(resolveNamedScopes(named, '/docs')).toEqual({
153
180
  allow: ['/docs'],
154
181
  deny: [],
182
+ explicitAllow: [],
183
+ explicitDeny: [],
155
184
  });
156
185
  });
157
186
  });
@@ -27,13 +27,22 @@ import type {
27
27
  */
28
28
  export function normalizeScopes(raw: unknown): NormalizedScopes | null {
29
29
  if (raw === undefined || raw === null) return null;
30
- if (typeof raw === 'string') return { allow: [raw], deny: [] };
31
- if (Array.isArray(raw)) return { allow: raw as string[], deny: [] };
30
+ if (typeof raw === 'string')
31
+ return { allow: [raw], deny: [], explicitAllow: [], explicitDeny: [] };
32
+ if (Array.isArray(raw))
33
+ return {
34
+ allow: raw as string[],
35
+ deny: [],
36
+ explicitAllow: [],
37
+ explicitDeny: [],
38
+ };
32
39
  if (typeof raw === 'object') {
33
40
  const obj = raw as { allow?: string[]; deny?: string[] };
34
41
  return {
35
42
  allow: obj.allow ?? ['/**'],
36
43
  deny: obj.deny ?? [],
44
+ explicitAllow: [],
45
+ explicitDeny: [],
37
46
  };
38
47
  }
39
48
  return null;
@@ -56,6 +65,8 @@ export function resolveNamedScopes(
56
65
  return {
57
66
  allow: overrides.allow ?? ['/**'],
58
67
  deny: overrides.deny ?? [],
68
+ explicitAllow: overrides.allow ?? [],
69
+ explicitDeny: overrides.deny ?? [],
59
70
  };
60
71
  }
61
72
  return null;
@@ -91,6 +102,8 @@ export function resolveNamedScopes(
91
102
  return {
92
103
  allow: allow.length > 0 ? allow : ['/**'],
93
104
  deny,
105
+ explicitAllow: overrides?.allow ?? [],
106
+ explicitDeny: overrides?.deny ?? [],
94
107
  };
95
108
  }
96
109
 
@@ -99,6 +112,8 @@ export function resolveNamedScopes(
99
112
  if (!normalized) return null;
100
113
  if (overrides?.allow) normalized.allow.push(...overrides.allow);
101
114
  if (overrides?.deny) normalized.deny.push(...overrides.deny);
115
+ normalized.explicitAllow = overrides?.allow ?? [];
116
+ normalized.explicitDeny = overrides?.deny ?? [];
102
117
  return normalized;
103
118
  }
104
119
 
@@ -11,10 +11,19 @@ export type { AuthMode, JeevesConfig };
11
11
  /**
12
12
  * Normalized scopes — always resolved to \{ allow, deny \} form.
13
13
  * null = unrestricted access.
14
+ *
15
+ * Explicit overrides take precedence over named scope patterns:
16
+ * 1. If explicitDeny matches → DENIED (overrides named allow)
17
+ * 2. If explicitAllow matches → ALLOWED (overrides named deny)
18
+ * 3. Otherwise: normal allow AND NOT deny evaluation
14
19
  */
15
20
  export interface NormalizedScopes {
16
21
  allow: string[];
17
22
  deny: string[];
23
+ /** Explicit allow patterns from the insider/key entry — override named scope denies. */
24
+ explicitAllow: string[];
25
+ /** Explicit deny patterns from the insider/key entry — override named scope allows. */
26
+ explicitDeny: string[];
18
27
  }
19
28
 
20
29
  /**
@@ -1 +0,0 @@
1
- <svg id="my-svg" width="100%" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="flowchart" style="max-width: 1102.22px; background-color: white;" viewBox="0 0 1102.21875 1878.078125" role="graphics-document document" aria-roledescription="flowchart-v2"><style>#my-svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#my-svg .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#my-svg .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#my-svg .error-icon{fill:#552222;}#my-svg .error-text{fill:#552222;stroke:#552222;}#my-svg .edge-thickness-normal{stroke-width:1px;}#my-svg .edge-thickness-thick{stroke-width:3.5px;}#my-svg .edge-pattern-solid{stroke-dasharray:0;}#my-svg .edge-thickness-invisible{stroke-width:0;fill:none;}#my-svg .edge-pattern-dashed{stroke-dasharray:3;}#my-svg .edge-pattern-dotted{stroke-dasharray:2;}#my-svg .marker{fill:#333333;stroke:#333333;}#my-svg .marker.cross{stroke:#333333;}#my-svg svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#my-svg p{margin:0;}#my-svg .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#my-svg .cluster-label text{fill:#333;}#my-svg .cluster-label span{color:#333;}#my-svg .cluster-label span p{background-color:transparent;}#my-svg .label text,#my-svg span{fill:#333;color:#333;}#my-svg .node rect,#my-svg .node circle,#my-svg .node ellipse,#my-svg .node polygon,#my-svg .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#my-svg .rough-node .label text,#my-svg .node .label text,#my-svg .image-shape .label,#my-svg .icon-shape .label{text-anchor:middle;}#my-svg .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#my-svg .rough-node .label,#my-svg .node .label,#my-svg .image-shape .label,#my-svg .icon-shape .label{text-align:center;}#my-svg .node.clickable{cursor:pointer;}#my-svg .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#my-svg .arrowheadPath{fill:#333333;}#my-svg .edgePath .path{stroke:#333333;stroke-width:2.0px;}#my-svg .flowchart-link{stroke:#333333;fill:none;}#my-svg .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#my-svg .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#my-svg .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#my-svg .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#my-svg .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#my-svg .cluster text{fill:#333;}#my-svg .cluster span{color:#333;}#my-svg div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#my-svg .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#my-svg rect.text{fill:none;stroke-width:0;}#my-svg .icon-shape,#my-svg .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#my-svg .icon-shape p,#my-svg .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#my-svg .icon-shape rect,#my-svg .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#my-svg .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#my-svg .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#my-svg :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}</style><g><marker id="my-svg_flowchart-v2-pointEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"/></marker><marker id="my-svg_flowchart-v2-pointStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="4.5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"><path d="M 0 5 L 10 10 L 10 0 z" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"/></marker><marker id="my-svg_flowchart-v2-circleEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="11" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"/></marker><marker id="my-svg_flowchart-v2-circleStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-1" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"/></marker><marker id="my-svg_flowchart-v2-crossEnd" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="12" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width: 2; stroke-dasharray: 1, 0;"/></marker><marker id="my-svg_flowchart-v2-crossStart" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="-1" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width: 2; stroke-dasharray: 1, 0;"/></marker><g class="root"><g class="clusters"/><g class="edgePaths"><path d="M551.109,62L551.109,66.167C551.109,70.333,551.109,78.667,551.109,86.333C551.109,94,551.109,101,551.109,104.5L551.109,108" id="L_A_B_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_A_B_0" data-points="W3sieCI6NTUxLjEwOTM3NSwieSI6NjJ9LHsieCI6NTUxLjEwOTM3NSwieSI6ODd9LHsieCI6NTUxLjEwOTM3NSwieSI6MTEyfV0=" marker-end="url(#my-svg_flowchart-v2-pointEnd)"/><path d="M464.771,303.661L431.068,324.218C397.365,344.774,329.96,385.887,296.257,411.944C262.555,438,262.555,449,262.555,454.5L262.555,460" id="L_B_C_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_B_C_0" data-points="W3sieCI6NDY0Ljc3MDU1OTI2NTg0NiwieSI6MzAzLjY2MTE4NDI2NTg0Nn0seyJ4IjoyNjIuNTU0Njg3NSwieSI6NDI3fSx7IngiOjI2Mi41NTQ2ODc1LCJ5Ijo0NjR9XQ==" marker-end="url(#my-svg_flowchart-v2-pointEnd)"/><path d="M211.818,635.342L199.515,649.964C187.212,664.587,162.606,693.833,150.303,730.622C138,767.411,138,811.745,138,833.911L138,856.078" id="L_C_D_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_C_D_0" data-points="W3sieCI6MjExLjgxODI1MDEwOTI2NTczLCJ5Ijo2MzUuMzQxNjg3NjA5MjY1N30seyJ4IjoxMzgsInkiOjcyMy4wNzgxMjV9LHsieCI6MTM4LCJ5Ijo4NjAuMDc4MTI1fV0=" marker-end="url(#my-svg_flowchart-v2-pointEnd)"/><path d="M313.291,635.342L325.594,649.964C337.897,664.587,362.503,693.833,374.806,732.622C387.109,771.411,387.109,819.745,387.109,843.911L387.109,868.078" id="L_C_E_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_C_E_0" data-points="W3sieCI6MzEzLjI5MTEyNDg5MDczNDI0LCJ5Ijo2MzUuMzQxNjg3NjA5MjY1N30seyJ4IjozODcuMTA5Mzc1LCJ5Ijo3MjMuMDc4MTI1fSx7IngiOjM4Ny4xMDkzNzUsInkiOjg3Mi4wNzgxMjV9XQ==" marker-end="url(#my-svg_flowchart-v2-pointEnd)"/><path d="M633.165,307.945L661.757,327.787C690.349,347.63,747.534,387.315,776.126,412.941C804.719,438.568,804.719,450.135,804.719,455.919L804.719,461.703" id="L_B_F_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_B_F_0" data-points="W3sieCI6NjMzLjE2NDYyMTQwODQzNzksInkiOjMwNy45NDQ3NTM1OTE1NjIxfSx7IngiOjgwNC43MTg3NSwieSI6NDI3fSx7IngiOjgwNC43MTg3NSwieSI6NDY1LjcwMzEyNX1d" marker-end="url(#my-svg_flowchart-v2-pointEnd)"/><path d="M748.013,627.67L730.881,643.571C713.749,659.473,679.484,691.275,662.351,712.677C645.219,734.078,645.219,745.078,645.219,750.578L645.219,756.078" id="L_F_G_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_F_G_0" data-points="W3sieCI6NzQ4LjAxMzQ5MTUyMTY1NjMsInkiOjYyNy42Njk3NDE1MjE2NTYzfSx7IngiOjY0NS4yMTg3NSwieSI6NzIzLjA3ODEyNX0seyJ4Ijo2NDUuMjE4NzUsInkiOjc2MC4wNzgxMjV9XQ==" marker-end="url(#my-svg_flowchart-v2-pointEnd)"/><path d="M579.137,971.996L563.567,989.176C547.997,1006.357,516.858,1040.717,501.288,1080.064C485.719,1119.411,485.719,1163.745,485.719,1185.911L485.719,1208.078" id="L_G_H_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_G_H_0" data-points="W3sieCI6NTc5LjEzNjc4Mjc4Njg4NTIsInkiOjk3MS45OTYxNTc3ODY4ODUyfSx7IngiOjQ4NS43MTg3NSwieSI6MTA3NS4wNzgxMjV9LHsieCI6NDg1LjcxODc1LCJ5IjoxMjEyLjA3ODEyNX1d" marker-end="url(#my-svg_flowchart-v2-pointEnd)"/><path d="M711.301,971.996L726.87,989.176C742.44,1006.357,773.579,1040.717,789.149,1063.398C804.719,1086.078,804.719,1097.078,804.719,1102.578L804.719,1108.078" id="L_G_I_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_G_I_0" data-points="W3sieCI6NzExLjMwMDcxNzIxMzExNDgsInkiOjk3MS45OTYxNTc3ODY4ODUyfSx7IngiOjgwNC43MTg3NSwieSI6MTA3NS4wNzgxMjV9LHsieCI6ODA0LjcxODc1LCJ5IjoxMTEyLjA3ODEyNX1d" marker-end="url(#my-svg_flowchart-v2-pointEnd)"/><path d="M745.914,1331.274L734.206,1347.241C722.497,1363.208,699.081,1395.143,687.372,1416.611C675.664,1438.078,675.664,1449.078,675.664,1454.578L675.664,1460.078" id="L_I_J_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_I_J_0" data-points="W3sieCI6NzQ1LjkxNDIwNjc1NzI0MTIsInkiOjEzMzEuMjczNTgxNzU3MjQxM30seyJ4Ijo2NzUuNjY0MDYyNSwieSI6MTQyNy4wNzgxMjV9LHsieCI6Njc1LjY2NDA2MjUsInkiOjE0NjQuMDc4MTI1fV0=" marker-end="url(#my-svg_flowchart-v2-pointEnd)"/><path d="M621.753,1688.167L612.153,1703.319C602.553,1718.471,583.352,1748.774,573.752,1769.426C564.152,1790.078,564.152,1801.078,564.152,1806.578L564.152,1812.078" id="L_J_K_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_J_K_0" data-points="W3sieCI6NjIxLjc1Mjc2ODEyMzQxMjEsInkiOjE2ODguMTY2ODMwNjIzNDEyMX0seyJ4Ijo1NjQuMTUyMzQzNzUsInkiOjE3NzkuMDc4MTI1fSx7IngiOjU2NC4xNTIzNDM3NSwieSI6MTgxNi4wNzgxMjV9XQ==" marker-end="url(#my-svg_flowchart-v2-pointEnd)"/><path d="M729.575,1688.167L739.175,1703.319C748.775,1718.471,767.976,1748.774,777.576,1769.426C787.176,1790.078,787.176,1801.078,787.176,1806.578L787.176,1812.078" id="L_J_L_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_J_L_0" data-points="W3sieCI6NzI5LjU3NTM1Njg3NjU4NzksInkiOjE2ODguMTY2ODMwNjIzNDEyMX0seyJ4Ijo3ODcuMTc1NzgxMjUsInkiOjE3NzkuMDc4MTI1fSx7IngiOjc4Ny4xNzU3ODEyNSwieSI6MTgxNi4wNzgxMjV9XQ==" marker-end="url(#my-svg_flowchart-v2-pointEnd)"/><path d="M863.523,1331.274L875.232,1347.241C886.94,1363.208,910.357,1395.143,922.065,1435.277C933.773,1475.411,933.773,1523.745,933.773,1547.911L933.773,1572.078" id="L_I_M_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_I_M_0" data-points="W3sieCI6ODYzLjUyMzI5MzI0Mjc1ODgsInkiOjEzMzEuMjczNTgxNzU3MjQxM30seyJ4Ijo5MzMuNzczNDM3NSwieSI6MTQyNy4wNzgxMjV9LHsieCI6OTMzLjc3MzQzNzUsInkiOjE1NzYuMDc4MTI1fV0=" marker-end="url(#my-svg_flowchart-v2-pointEnd)"/><path d="M861.424,627.67L878.556,643.571C895.689,659.473,929.954,691.275,947.086,729.343C964.219,767.411,964.219,811.745,964.219,833.911L964.219,856.078" id="L_F_N_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_F_N_0" data-points="W3sieCI6ODYxLjQyNDAwODQ3ODM0MzcsInkiOjYyNy42Njk3NDE1MjE2NTYzfSx7IngiOjk2NC4yMTg3NSwieSI6NzIzLjA3ODEyNX0seyJ4Ijo5NjQuMjE4NzUsInkiOjg2MC4wNzgxMjV9XQ==" marker-end="url(#my-svg_flowchart-v2-pointEnd)"/></g><g class="edgeLabels"><g class="edgeLabel"><g class="label" data-id="L_A_B_0" transform="translate(0, 0)"><foreignObject width="0" height="0"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel" transform="translate(262.5546875, 427)"><g class="label" data-id="L_B_C_0" transform="translate(-11.328125, -12)"><foreignObject width="22.65625" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel"><p>Yes</p></span></div></foreignObject></g></g><g class="edgeLabel" transform="translate(138, 723.078125)"><g class="label" data-id="L_C_D_0" transform="translate(-11.328125, -12)"><foreignObject width="22.65625" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel"><p>Yes</p></span></div></foreignObject></g></g><g class="edgeLabel" transform="translate(387.109375, 723.078125)"><g class="label" data-id="L_C_E_0" transform="translate(-9.3984375, -12)"><foreignObject width="18.796875" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel"><p>No</p></span></div></foreignObject></g></g><g class="edgeLabel" transform="translate(804.71875, 427)"><g class="label" data-id="L_B_F_0" transform="translate(-9.3984375, -12)"><foreignObject width="18.796875" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel"><p>No</p></span></div></foreignObject></g></g><g class="edgeLabel" transform="translate(645.21875, 723.078125)"><g class="label" data-id="L_F_G_0" transform="translate(-11.328125, -12)"><foreignObject width="22.65625" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel"><p>Yes</p></span></div></foreignObject></g></g><g class="edgeLabel" transform="translate(485.71875, 1075.078125)"><g class="label" data-id="L_G_H_0" transform="translate(-11.328125, -12)"><foreignObject width="22.65625" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel"><p>Yes</p></span></div></foreignObject></g></g><g class="edgeLabel" transform="translate(804.71875, 1075.078125)"><g class="label" data-id="L_G_I_0" transform="translate(-9.3984375, -12)"><foreignObject width="18.796875" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel"><p>No</p></span></div></foreignObject></g></g><g class="edgeLabel" transform="translate(675.6640625, 1427.078125)"><g class="label" data-id="L_I_J_0" transform="translate(-11.328125, -12)"><foreignObject width="22.65625" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel"><p>Yes</p></span></div></foreignObject></g></g><g class="edgeLabel" transform="translate(564.15234375, 1779.078125)"><g class="label" data-id="L_J_K_0" transform="translate(-11.328125, -12)"><foreignObject width="22.65625" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel"><p>Yes</p></span></div></foreignObject></g></g><g class="edgeLabel" transform="translate(787.17578125, 1779.078125)"><g class="label" data-id="L_J_L_0" transform="translate(-9.3984375, -12)"><foreignObject width="18.796875" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel"><p>No</p></span></div></foreignObject></g></g><g class="edgeLabel" transform="translate(933.7734375, 1427.078125)"><g class="label" data-id="L_I_M_0" transform="translate(-9.3984375, -12)"><foreignObject width="18.796875" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel"><p>No</p></span></div></foreignObject></g></g><g class="edgeLabel" transform="translate(964.21875, 723.078125)"><g class="label" data-id="L_F_N_0" transform="translate(-9.3984375, -12)"><foreignObject width="18.796875" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel"><p>No</p></span></div></foreignObject></g></g></g><g class="nodes"><g class="node default" id="flowchart-A-0" transform="translate(551.109375, 35)"><rect class="basic label-container" style="" x="-84.9296875" y="-27" width="169.859375" height="54"/><g class="label" style="" transform="translate(-54.9296875, -12)"><rect/><foreignObject width="109.859375" height="24"><div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="nodeLabel"><p>Request arrives</p></span></div></foreignObject></g></g><g class="node default" id="flowchart-B-1" transform="translate(551.109375, 251)"><polygon points="139,0 278,-139 139,-278 0,-139" class="label-container" transform="translate(-138.5, 139)"/><g class="label" style="" transform="translate(-100, -24)"><rect/><foreignObject width="200" height="48"><div xmlns="http://www.w3.org/1999/xhtml" style="display: table; white-space: break-spaces; line-height: 1.5; max-width: 200px; text-align: center; width: 200px;"><span class="nodeLabel"><p>Has session cookie?\n(Google OAuth)</p></span></div></foreignObject></g></g><g class="node default" id="flowchart-C-3" transform="translate(262.5546875, 575.0390625)"><polygon points="111.0390625,0 222.078125,-111.0390625 111.0390625,-222.078125 0,-111.0390625" class="label-container" transform="translate(-110.5390625, 111.0390625)"/><g class="label" style="" transform="translate(-84.0390625, -12)"><rect/><foreignObject width="168.078125" height="24"><div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="nodeLabel"><p>Email in\ninsiders map?</p></span></div></foreignObject></g></g><g class="node default" id="flowchart-D-5" transform="translate(138, 899.078125)"><rect class="basic label-container" style="fill:#22c55e !important;stroke:#16a34a !important" x="-130" y="-39" width="260" height="78"/><g class="label" style="color:#fff !important" transform="translate(-100, -24)"><rect/><foreignObject width="200" height="48"><div style="color: rgb(255, 255, 255) !important; display: table; white-space: break-spaces; line-height: 1.5; max-width: 200px; text-align: center; width: 200px;" xmlns="http://www.w3.org/1999/xhtml"><span style="color:#fff !important" class="nodeLabel"><p>✅ INSIDER access\n(within scopes)</p></span></div></foreignObject></g></g><g class="node default" id="flowchart-E-7" transform="translate(387.109375, 899.078125)"><rect class="basic label-container" style="fill:#ef4444 !important;stroke:#dc2626 !important" x="-69.109375" y="-27" width="138.21875" height="54"/><g class="label" style="color:#fff !important" transform="translate(-39.109375, -12)"><rect/><foreignObject width="78.21875" height="24"><div style="color: rgb(255, 255, 255) !important; display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span style="color:#fff !important" class="nodeLabel"><p>❌ DENIED</p></span></div></foreignObject></g></g><g class="node default" id="flowchart-F-9" transform="translate(804.71875, 575.0390625)"><polygon points="109.3359375,0 218.671875,-109.3359375 109.3359375,-218.671875 0,-109.3359375" class="label-container" transform="translate(-108.8359375, 109.3359375)"/><g class="label" style="" transform="translate(-82.3359375, -12)"><rect/><foreignObject width="164.671875" height="24"><div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="nodeLabel"><p>Has ?key=\nparameter?</p></span></div></foreignObject></g></g><g class="node default" id="flowchart-G-11" transform="translate(645.21875, 899.078125)"><polygon points="139,0 278,-139 139,-278 0,-139" class="label-container" transform="translate(-138.5, 139)"/><g class="label" style="" transform="translate(-100, -24)"><rect/><foreignObject width="200" height="48"><div xmlns="http://www.w3.org/1999/xhtml" style="display: table; white-space: break-spaces; line-height: 1.5; max-width: 200px; text-align: center; width: 200px;"><span class="nodeLabel"><p>Matches machine\ninsider key?</p></span></div></foreignObject></g></g><g class="node default" id="flowchart-H-13" transform="translate(485.71875, 1251.078125)"><rect class="basic label-container" style="fill:#22c55e !important;stroke:#16a34a !important" x="-130" y="-39" width="260" height="78"/><g class="label" style="color:#fff !important" transform="translate(-100, -24)"><rect/><foreignObject width="200" height="48"><div style="color: rgb(255, 255, 255) !important; display: table; white-space: break-spaces; line-height: 1.5; max-width: 200px; text-align: center; width: 200px;" xmlns="http://www.w3.org/1999/xhtml"><span style="color:#fff !important" class="nodeLabel"><p>✅ INSIDER access\n(within scopes)</p></span></div></foreignObject></g></g><g class="node default" id="flowchart-I-15" transform="translate(804.71875, 1251.078125)"><polygon points="139,0 278,-139 139,-278 0,-139" class="label-container" transform="translate(-138.5, 139)"/><g class="label" style="" transform="translate(-100, -24)"><rect/><foreignObject width="200" height="48"><div xmlns="http://www.w3.org/1999/xhtml" style="display: table; white-space: break-spaces; line-height: 1.5; max-width: 200px; text-align: center; width: 200px;"><span class="nodeLabel"><p>Matches outsider key?\n(machine or insider seed)</p></span></div></foreignObject></g></g><g class="node default" id="flowchart-J-17" transform="translate(675.6640625, 1603.078125)"><polygon points="139,0 278,-139 139,-278 0,-139" class="label-container" transform="translate(-138.5, 139)"/><g class="label" style="" transform="translate(-100, -24)"><rect/><foreignObject width="200" height="48"><div xmlns="http://www.w3.org/1999/xhtml" style="display: table; white-space: break-spaces; line-height: 1.5; max-width: 200px; text-align: center; width: 200px;"><span class="nodeLabel"><p>Path matches key\npath or ancestor?</p></span></div></foreignObject></g></g><g class="node default" id="flowchart-K-19" transform="translate(564.15234375, 1843.078125)"><rect class="basic label-container" style="fill:#22c55e !important;stroke:#16a34a !important" x="-103.9140625" y="-27" width="207.828125" height="54"/><g class="label" style="color:#fff !important" transform="translate(-73.9140625, -12)"><rect/><foreignObject width="147.828125" height="24"><div style="color: rgb(255, 255, 255) !important; display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span style="color:#fff !important" class="nodeLabel"><p>✅ OUTSIDER access</p></span></div></foreignObject></g></g><g class="node default" id="flowchart-L-21" transform="translate(787.17578125, 1843.078125)"><rect class="basic label-container" style="fill:#ef4444 !important;stroke:#dc2626 !important" x="-69.109375" y="-27" width="138.21875" height="54"/><g class="label" style="color:#fff !important" transform="translate(-39.109375, -12)"><rect/><foreignObject width="78.21875" height="24"><div style="color: rgb(255, 255, 255) !important; display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span style="color:#fff !important" class="nodeLabel"><p>❌ DENIED</p></span></div></foreignObject></g></g><g class="node default" id="flowchart-M-23" transform="translate(933.7734375, 1603.078125)"><rect class="basic label-container" style="fill:#ef4444 !important;stroke:#dc2626 !important" x="-69.109375" y="-27" width="138.21875" height="54"/><g class="label" style="color:#fff !important" transform="translate(-39.109375, -12)"><rect/><foreignObject width="78.21875" height="24"><div style="color: rgb(255, 255, 255) !important; display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span style="color:#fff !important" class="nodeLabel"><p>❌ DENIED</p></span></div></foreignObject></g></g><g class="node default" id="flowchart-N-25" transform="translate(964.21875, 899.078125)"><rect class="basic label-container" style="fill:#f59e0b !important;stroke:#d97706 !important" x="-130" y="-39" width="260" height="78"/><g class="label" style="color:#fff !important" transform="translate(-100, -24)"><rect/><foreignObject width="200" height="48"><div style="color: rgb(255, 255, 255) !important; display: table; white-space: break-spaces; line-height: 1.5; max-width: 200px; text-align: center; width: 200px;" xmlns="http://www.w3.org/1999/xhtml"><span style="color:#fff !important" class="nodeLabel"><p>❌ DENIED\n(or redirect to Google login)</p></span></div></foreignObject></g></g></g></g></g></svg>
@@ -1 +0,0 @@
1
- <svg id="my-svg" width="100%" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="flowchart" style="max-width: 789.625px; background-color: white;" viewBox="0 0 789.625 1542.296875" role="graphics-document document" aria-roledescription="flowchart-v2"><style>#my-svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#my-svg .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#my-svg .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#my-svg .error-icon{fill:#552222;}#my-svg .error-text{fill:#552222;stroke:#552222;}#my-svg .edge-thickness-normal{stroke-width:1px;}#my-svg .edge-thickness-thick{stroke-width:3.5px;}#my-svg .edge-pattern-solid{stroke-dasharray:0;}#my-svg .edge-thickness-invisible{stroke-width:0;fill:none;}#my-svg .edge-pattern-dashed{stroke-dasharray:3;}#my-svg .edge-pattern-dotted{stroke-dasharray:2;}#my-svg .marker{fill:#333333;stroke:#333333;}#my-svg .marker.cross{stroke:#333333;}#my-svg svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#my-svg p{margin:0;}#my-svg .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#my-svg .cluster-label text{fill:#333;}#my-svg .cluster-label span{color:#333;}#my-svg .cluster-label span p{background-color:transparent;}#my-svg .label text,#my-svg span{fill:#333;color:#333;}#my-svg .node rect,#my-svg .node circle,#my-svg .node ellipse,#my-svg .node polygon,#my-svg .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#my-svg .rough-node .label text,#my-svg .node .label text,#my-svg .image-shape .label,#my-svg .icon-shape .label{text-anchor:middle;}#my-svg .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#my-svg .rough-node .label,#my-svg .node .label,#my-svg .image-shape .label,#my-svg .icon-shape .label{text-align:center;}#my-svg .node.clickable{cursor:pointer;}#my-svg .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#my-svg .arrowheadPath{fill:#333333;}#my-svg .edgePath .path{stroke:#333333;stroke-width:2.0px;}#my-svg .flowchart-link{stroke:#333333;fill:none;}#my-svg .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#my-svg .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#my-svg .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#my-svg .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#my-svg .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#my-svg .cluster text{fill:#333;}#my-svg .cluster span{color:#333;}#my-svg div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#my-svg .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#my-svg rect.text{fill:none;stroke-width:0;}#my-svg .icon-shape,#my-svg .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#my-svg .icon-shape p,#my-svg .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#my-svg .icon-shape rect,#my-svg .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#my-svg .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#my-svg .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#my-svg :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}</style><g><marker id="my-svg_flowchart-v2-pointEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"/></marker><marker id="my-svg_flowchart-v2-pointStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="4.5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"><path d="M 0 5 L 10 10 L 10 0 z" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"/></marker><marker id="my-svg_flowchart-v2-circleEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="11" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"/></marker><marker id="my-svg_flowchart-v2-circleStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-1" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"/></marker><marker id="my-svg_flowchart-v2-crossEnd" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="12" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width: 2; stroke-dasharray: 1, 0;"/></marker><marker id="my-svg_flowchart-v2-crossStart" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="-1" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width: 2; stroke-dasharray: 1, 0;"/></marker><g class="root"><g class="clusters"/><g class="edgePaths"><path d="M394.813,86L394.813,90.167C394.813,94.333,394.813,102.667,394.813,110.333C394.813,118,394.813,125,394.813,128.5L394.813,132" id="L_A_B_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_A_B_0" data-points="W3sieCI6Mzk0LjgxMjUsInkiOjg2fSx7IngiOjM5NC44MTI1LCJ5IjoxMTF9LHsieCI6Mzk0LjgxMjUsInkiOjEzNn1d" marker-end="url(#my-svg_flowchart-v2-pointEnd)"/><path d="M394.813,214L394.813,218.167C394.813,222.333,394.813,230.667,394.813,238.333C394.813,246,394.813,253,394.813,256.5L394.813,260" id="L_B_C_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_B_C_0" data-points="W3sieCI6Mzk0LjgxMjUsInkiOjIxNH0seyJ4IjozOTQuODEyNSwieSI6MjM5fSx7IngiOjM5NC44MTI1LCJ5IjoyNjR9XQ==" marker-end="url(#my-svg_flowchart-v2-pointEnd)"/><path d="M394.813,542L394.813,546.167C394.813,550.333,394.813,558.667,394.813,566.333C394.813,574,394.813,581,394.813,584.5L394.813,588" id="L_C_D_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_C_D_0" data-points="W3sieCI6Mzk0LjgxMjUsInkiOjU0Mn0seyJ4IjozOTQuODEyNSwieSI6NTY3fSx7IngiOjM5NC44MTI1LCJ5Ijo1OTJ9XQ==" marker-end="url(#my-svg_flowchart-v2-pointEnd)"/><path d="M394.813,694L394.813,698.167C394.813,702.333,394.813,710.667,394.813,718.333C394.813,726,394.813,733,394.813,736.5L394.813,740" id="L_D_E_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_D_E_0" data-points="W3sieCI6Mzk0LjgxMjUsInkiOjY5NH0seyJ4IjozOTQuODEyNSwieSI6NzE5fSx7IngiOjM5NC44MTI1LCJ5Ijo3NDR9XQ==" marker-end="url(#my-svg_flowchart-v2-pointEnd)"/><path d="M364.406,816.234L348.073,827.469C331.74,838.703,299.073,861.172,282.74,877.906C266.406,894.641,266.406,905.641,266.406,911.141L266.406,916.641" id="L_E_F_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_E_F_0" data-points="W3sieCI6MzY0LjQwNjIxODQ1ODIzODcsInkiOjgxNi4yMzQzNDM0NTgyMzg4fSx7IngiOjI2Ni40MDYyNSwieSI6ODgzLjY0MDYyNX0seyJ4IjoyNjYuNDA2MjUsInkiOjkyMC42NDA2MjV9XQ==" marker-end="url(#my-svg_flowchart-v2-pointEnd)"/><path d="M217.181,1077.072L203.985,1091.443C190.788,1105.814,164.394,1134.555,151.197,1154.426C138,1174.297,138,1185.297,138,1190.797L138,1196.297" id="L_F_G_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_F_G_0" data-points="W3sieCI6MjE3LjE4MTQ4NTU1MzY3ODU2LCJ5IjoxMDc3LjA3MjExMDU1MzY3ODZ9LHsieCI6MTM4LCJ5IjoxMTYzLjI5Njg3NX0seyJ4IjoxMzgsInkiOjEyMDAuMjk2ODc1fV0=" marker-end="url(#my-svg_flowchart-v2-pointEnd)"/><path d="M315.631,1077.072L328.828,1091.443C342.025,1105.814,368.419,1134.555,381.616,1156.426C394.813,1178.297,394.813,1193.297,394.813,1200.797L394.813,1208.297" id="L_F_H_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_F_H_0" data-points="W3sieCI6MzE1LjYzMTAxNDQ0NjMyMTQ0LCJ5IjoxMDc3LjA3MjExMDU1MzY3ODZ9LHsieCI6Mzk0LjgxMjUsInkiOjExNjMuMjk2ODc1fSx7IngiOjM5NC44MTI1LCJ5IjoxMjEyLjI5Njg3NX1d" marker-end="url(#my-svg_flowchart-v2-pointEnd)"/><path d="M138,1278.297L138,1282.464C138,1286.63,138,1294.964,145.763,1302.999C153.526,1311.035,169.052,1318.774,176.816,1322.643L184.579,1326.513" id="L_G_I_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_G_I_0" data-points="W3sieCI6MTM4LCJ5IjoxMjc4LjI5Njg3NX0seyJ4IjoxMzgsInkiOjEzMDMuMjk2ODc1fSx7IngiOjE4OC4xNTg2OTE0MDYyNSwieSI6MTMyOC4yOTY4NzV9XQ==" marker-end="url(#my-svg_flowchart-v2-pointEnd)"/><path d="M394.813,1266.297L394.813,1272.464C394.813,1278.63,394.813,1290.964,387.049,1300.999C379.286,1311.035,363.76,1318.774,355.997,1322.643L348.234,1326.513" id="L_H_I_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_H_I_0" data-points="W3sieCI6Mzk0LjgxMjUsInkiOjEyNjYuMjk2ODc1fSx7IngiOjM5NC44MTI1LCJ5IjoxMzAzLjI5Njg3NX0seyJ4IjozNDQuNjUzODA4NTkzNzUsInkiOjEzMjguMjk2ODc1fV0=" marker-end="url(#my-svg_flowchart-v2-pointEnd)"/><path d="M266.406,1406.297L266.406,1410.464C266.406,1414.63,266.406,1422.964,266.406,1430.63C266.406,1438.297,266.406,1445.297,266.406,1448.797L266.406,1452.297" id="L_I_J_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_I_J_0" data-points="W3sieCI6MjY2LjQwNjI1LCJ5IjoxNDA2LjI5Njg3NX0seyJ4IjoyNjYuNDA2MjUsInkiOjE0MzEuMjk2ODc1fSx7IngiOjI2Ni40MDYyNSwieSI6MTQ1Ni4yOTY4NzV9XQ==" marker-end="url(#my-svg_flowchart-v2-pointEnd)"/><path d="M433,808.453L469.437,820.985C505.875,833.516,578.75,858.578,615.187,889.247C651.625,919.917,651.625,956.193,651.625,974.331L651.625,992.469" id="L_E_K_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_E_K_0" data-points="W3sieCI6NDMyLjk5OTgyMTczOTgxOTM3LCJ5Ijo4MDguNDUzMzAzMjYwMTgwNn0seyJ4Ijo2NTEuNjI1LCJ5Ijo4ODMuNjQwNjI1fSx7IngiOjY1MS42MjUsInkiOjk5Ni40Njg3NX1d" marker-end="url(#my-svg_flowchart-v2-pointEnd)"/><path d="M651.625,1050.469L651.625,1069.273C651.625,1088.078,651.625,1125.688,651.625,1149.992C651.625,1174.297,651.625,1185.297,651.625,1190.797L651.625,1196.297" id="L_K_L_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_K_L_0" data-points="W3sieCI6NjUxLjYyNSwieSI6MTA1MC40Njg3NX0seyJ4Ijo2NTEuNjI1LCJ5IjoxMTYzLjI5Njg3NX0seyJ4Ijo2NTEuNjI1LCJ5IjoxMjAwLjI5Njg3NX1d" marker-end="url(#my-svg_flowchart-v2-pointEnd)"/></g><g class="edgeLabels"><g class="edgeLabel"><g class="label" data-id="L_A_B_0" transform="translate(0, 0)"><foreignObject width="0" height="0"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g class="label" data-id="L_B_C_0" transform="translate(0, 0)"><foreignObject width="0" height="0"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g class="label" data-id="L_C_D_0" transform="translate(0, 0)"><foreignObject width="0" height="0"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g class="label" data-id="L_D_E_0" transform="translate(0, 0)"><foreignObject width="0" height="0"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel" transform="translate(266.40625, 883.640625)"><g class="label" data-id="L_E_F_0" transform="translate(-59.015625, -12)"><foreignObject width="118.03125" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel"><p>First match wins</p></span></div></foreignObject></g></g><g class="edgeLabel" transform="translate(138, 1163.296875)"><g class="label" data-id="L_F_G_0" transform="translate(-11.328125, -12)"><foreignObject width="22.65625" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel"><p>Yes</p></span></div></foreignObject></g></g><g class="edgeLabel" transform="translate(394.8125, 1163.296875)"><g class="label" data-id="L_F_H_0" transform="translate(-9.3984375, -12)"><foreignObject width="18.796875" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel"><p>No</p></span></div></foreignObject></g></g><g class="edgeLabel"><g class="label" data-id="L_G_I_0" transform="translate(0, 0)"><foreignObject width="0" height="0"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g class="label" data-id="L_H_I_0" transform="translate(0, 0)"><foreignObject width="0" height="0"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g class="label" data-id="L_I_J_0" transform="translate(0, 0)"><foreignObject width="0" height="0"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel" transform="translate(651.625, 883.640625)"><g class="label" data-id="L_E_K_0" transform="translate(-34.15625, -12)"><foreignObject width="68.3125" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel"><p>No match</p></span></div></foreignObject></g></g><g class="edgeLabel"><g class="label" data-id="L_K_L_0" transform="translate(0, 0)"><foreignObject width="0" height="0"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel"></span></div></foreignObject></g></g></g><g class="nodes"><g class="node default" id="flowchart-A-0" transform="translate(394.8125, 47)"><rect class="basic label-container" style="fill:#4a9eff !important;stroke:#2d7dd2 !important" x="-130" y="-39" width="260" height="78"/><g class="label" style="color:#fff !important" transform="translate(-100, -24)"><rect/><foreignObject width="200" height="48"><div style="color: rgb(255, 255, 255) !important; display: table; white-space: break-spaces; line-height: 1.5; max-width: 200px; text-align: center; width: 200px;" xmlns="http://www.w3.org/1999/xhtml"><span style="color:#fff !important" class="nodeLabel"><p>POST /event?key=‹scoped-key›</p></span></div></foreignObject></g></g><g class="node default" id="flowchart-B-1" transform="translate(394.8125, 175)"><rect class="basic label-container" style="" x="-130" y="-39" width="260" height="78"/><g class="label" style="" transform="translate(-100, -24)"><rect/><foreignObject width="200" height="48"><div xmlns="http://www.w3.org/1999/xhtml" style="display: table; white-space: break-spaces; line-height: 1.5; max-width: 200px; text-align: center; width: 200px;"><span class="nodeLabel"><p>Authenticate key\n(check scope includes /event)</p></span></div></foreignObject></g></g><g class="node default" id="flowchart-C-3" transform="translate(394.8125, 403)"><polygon points="139,0 278,-139 139,-278 0,-139" class="label-container" transform="translate(-138.5, 139)"/><g class="label" style="" transform="translate(-100, -24)"><rect/><foreignObject width="200" height="48"><div xmlns="http://www.w3.org/1999/xhtml" style="display: table; white-space: break-spaces; line-height: 1.5; max-width: 200px; text-align: center; width: 200px;"><span class="nodeLabel"><p>For each event\nin config.events</p></span></div></foreignObject></g></g><g class="node default" id="flowchart-D-5" transform="translate(394.8125, 643)"><rect class="basic label-container" style="" x="-130" y="-51" width="260" height="102"/><g class="label" style="" transform="translate(-100, -36)"><rect/><foreignObject width="200" height="72"><div xmlns="http://www.w3.org/1999/xhtml" style="display: table; white-space: break-spaces; line-height: 1.5; max-width: 200px; text-align: center; width: 200px;"><span class="nodeLabel"><p>Validate body against\nevent.schema (ajv)</p></span></div></foreignObject></g></g><g class="node default" id="flowchart-E-7" transform="translate(394.8125, 795.3203125)"><polygon points="51.3203125,0 102.640625,-51.3203125 51.3203125,-102.640625 0,-51.3203125" class="label-container" transform="translate(-50.8203125, 51.3203125)"/><g class="label" style="" transform="translate(-24.3203125, -12)"><rect/><foreignObject width="48.640625" height="24"><div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="nodeLabel"><p>Match?</p></span></div></foreignObject></g></g><g class="node default" id="flowchart-F-9" transform="translate(266.40625, 1023.46875)"><polygon points="102.828125,0 205.65625,-102.828125 102.828125,-205.65625 0,-102.828125" class="label-container" transform="translate(-102.328125, 102.828125)"/><g class="label" style="" transform="translate(-75.828125, -12)"><rect/><foreignObject width="151.65625" height="24"><div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="nodeLabel"><p>event.map\ndefined?</p></span></div></foreignObject></g></g><g class="node default" id="flowchart-G-11" transform="translate(138, 1239.296875)"><rect class="basic label-container" style="" x="-130" y="-39" width="260" height="78"/><g class="label" style="" transform="translate(-100, -24)"><rect/><foreignObject width="200" height="48"><div xmlns="http://www.w3.org/1999/xhtml" style="display: table; white-space: break-spaces; line-height: 1.5; max-width: 200px; text-align: center; width: 200px;"><span class="nodeLabel"><p>Transform body\nvia JsonMap</p></span></div></foreignObject></g></g><g class="node default" id="flowchart-H-13" transform="translate(394.8125, 1239.296875)"><rect class="basic label-container" style="" x="-76.8125" y="-27" width="153.625" height="54"/><g class="label" style="" transform="translate(-46.8125, -12)"><rect/><foreignObject width="93.625" height="24"><div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="nodeLabel"><p>Use full body</p></span></div></foreignObject></g></g><g class="node default" id="flowchart-I-15" transform="translate(266.40625, 1367.296875)"><rect class="basic label-container" style="" x="-130" y="-39" width="260" height="78"/><g class="label" style="" transform="translate(-100, -24)"><rect/><foreignObject width="200" height="48"><div xmlns="http://www.w3.org/1999/xhtml" style="display: table; white-space: break-spaces; line-height: 1.5; max-width: 200px; text-align: center; width: 200px;"><span class="nodeLabel"><p>Append to durable\nJSONL queue</p></span></div></foreignObject></g></g><g class="node default" id="flowchart-J-19" transform="translate(266.40625, 1495.296875)"><rect class="basic label-container" style="fill:#22c55e !important;stroke:#16a34a !important" x="-130" y="-39" width="260" height="78"/><g class="label" style="color:#fff !important" transform="translate(-100, -24)"><rect/><foreignObject width="200" height="48"><div style="color: rgb(255, 255, 255) !important; display: table; white-space: break-spaces; line-height: 1.5; max-width: 200px; text-align: center; width: 200px;" xmlns="http://www.w3.org/1999/xhtml"><span style="color:#fff !important" class="nodeLabel"><p>Return 200\n{ matched: ‹event-name› }</p></span></div></foreignObject></g></g><g class="node default" id="flowchart-K-21" transform="translate(651.625, 1023.46875)"><rect class="basic label-container" style="" x="-94.53125" y="-27" width="189.0625" height="54"/><g class="label" style="" transform="translate(-64.53125, -12)"><rect/><foreignObject width="129.0625" height="24"><div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="nodeLabel"><p>Log as unmatched</p></span></div></foreignObject></g></g><g class="node default" id="flowchart-L-23" transform="translate(651.625, 1239.296875)"><rect class="basic label-container" style="fill:#f59e0b !important;stroke:#d97706 !important" x="-130" y="-39" width="260" height="78"/><g class="label" style="color:#fff !important" transform="translate(-100, -24)"><rect/><foreignObject width="200" height="48"><div style="color: rgb(255, 255, 255) !important; display: table; white-space: break-spaces; line-height: 1.5; max-width: 200px; text-align: center; width: 200px;" xmlns="http://www.w3.org/1999/xhtml"><span style="color:#fff !important" class="nodeLabel"><p>Return 200\n{ matched: null }</p></span></div></foreignObject></g></g></g></g></g></svg>