@gotcos/glasses-server 7.0.27 → 7.0.30

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.
@@ -43,9 +43,21 @@ export interface WebKeyRouterDeps {
43
43
  isWebRequest: (req: Request) => boolean
44
44
  apiToken: () => string
45
45
  selfDnsName: () => string | null
46
+ /**
47
+ * 7.0.14: when selfDnsName() is null, GET status (after its auth check) awaits this before it
48
+ * answers, so a boot-time miss does not hand COS Control `origin: null` (lib/tailnet-self-name.ts
49
+ * refreshIfMissing: bounded, single-flight, rate-limited). Never called while a name is known.
50
+ */
51
+ refreshSelfDnsName?: () => Promise<unknown>
46
52
  expectedOrigin: () => string | null
47
53
  /** Served-asset integrity (Phase 6) for Control's audit; absent: not reported. */
48
54
  integrityStatus?: () => Record<string, unknown>
55
+ /**
56
+ * B2: whether 3144 presents a publicly trusted certificate for this Mac's tailnet name, and why not
57
+ * (lib/web-key-signer-tls.ts SignerTlsStatus, e.g. `tailnet_https_certs_off`). Authority mode only;
58
+ * absent or null (no 3144 listener): not reported.
59
+ */
60
+ signerTls?: () => object | null
49
61
  /**
50
62
  * Phase 3: whether this request reached the 3144 signer listener (index.ts: the socket's local
51
63
  * port). Given, the phone's key steps (enroll start and prove, nonce) and the handoff claim answer
@@ -97,13 +109,27 @@ export function createWebKeyRouter(deps: WebKeyRouterDeps): Router {
97
109
  next()
98
110
  }
99
111
 
100
- router.get('/web-key/status', anchorRoutes, (req, res) => {
112
+ // 7.0.14: a request that passed the status route's own auth check, while this Mac's tailnet name is
113
+ // unknown, waits for one bounded lookup instead of answering a stale miss. Known name: no wait.
114
+ const learnSelfName = async (): Promise<void> => {
115
+ if (!deps.refreshSelfDnsName || deps.selfDnsName() !== null) return
116
+ try { await deps.refreshSelfDnsName() } catch { /* answer with what is known */ }
117
+ }
118
+
119
+ const signerTlsField = (): { signerTls?: object } => {
120
+ let state: object | null = null
121
+ try { state = deps.signerTls?.() ?? null } catch { state = null }
122
+ return state ? { signerTls: state } : {}
123
+ }
124
+
125
+ router.get('/web-key/status', anchorRoutes, async (req, res) => {
101
126
  const registry = deps.authority.registry
102
- const selfDns = deps.selfDnsName()
103
127
  if (deps.authority.mode() !== 'authority') {
104
128
  // Bootstrap only: COS Control's anchor state, nothing that suggests approvals. Token only:
105
129
  // no web page needs it while no phone can enroll. `enabled: false` keeps older Controls hidden.
106
130
  if (!hasToken(req)) return refuse(res, 401, 'unauthorized')
131
+ await learnSelfName()
132
+ const selfDns = deps.selfDnsName()
107
133
  const anchor = registry.anchor()
108
134
  return void res.json({
109
135
  ok: true,
@@ -118,6 +144,8 @@ export function createWebKeyRouter(deps: WebKeyRouterDeps): Router {
118
144
  })
119
145
  }
120
146
  if (!hasToken(req) && !web(req)) return refuse(res, 401, 'unauthorized')
147
+ await learnSelfName()
148
+ const selfDns = deps.selfDnsName()
121
149
  res.json({
122
150
  ok: true,
123
151
  enabled: true,
@@ -131,6 +159,7 @@ export function createWebKeyRouter(deps: WebKeyRouterDeps): Router {
131
159
  epoch: registry.epoch(),
132
160
  activeKeys: registry.activeCount(selfDns),
133
161
  ...(deps.integrityStatus ? { integrity: deps.integrityStatus() } : {}),
162
+ ...signerTlsField(),
134
163
  })
135
164
  })
136
165
 
@@ -2,43 +2,43 @@
2
2
  "v": 1,
3
3
  "algorithm": "sha256",
4
4
  "files": {
5
- "/app.css": "a4dbdb42ba6c9de5dc02235985fde499854693c41fca54ad3f3395587ab9834a",
6
- "/app.js": "9a26933a5bd72724b620c9c61d11508d895f63ddea620df684f62bb585d761de",
5
+ "/app.css": "bc5e29be841629261889aebfd439a3b126a50041c4e079c9866eac93a3552fe1",
6
+ "/app.js": "eb9e4b98ddf4982644c57ce7de11fb9a606e245b5e2169bb25adbe825c05773d",
7
7
  "/apple-touch-icon.png": "04ec7eca0bf6f53d89900d146ac32d4826759d6258bcdbd1ccdcc517f26f532c",
8
- "/control.css": "e7b287aa4acc1b3895a9ce61512fe449c555b6335a7145684fd78108ae008c75",
8
+ "/control.css": "f5b7a2e0f800cc07072581b7e817fcd2b54bdb1d91f4662a3cdc9d80a7bc32ff",
9
9
  "/fonts/DMSans.ttf": "8cd08d97e89c24d0aa92edd2f0f4c8ee6195eee9b7c9f154865a58b02f0c1c0d",
10
10
  "/fonts/Fraunces.ttf": "177ff6c0f14e5550a3c624247cd1189611d4eb65d000b14944c63d967958abbb",
11
11
  "/fonts/JetBrainsMono.ttf": "48715a42ec242c21e9f02692891e147d022299a52e48d5e413e1a942193ffeda",
12
- "/index.html": "736f56bbeec449f2204b815594d74042924622ef3004227202a0374df8bc82b5",
12
+ "/index.html": "c3a88479e5b9efa323129c5a425dada70dc7f0f27a0c787c1bc225c661f20a7b",
13
13
  "/mark.svg": "8e380d33d393027df192eb1afa08f15a380c2070c72d68f27311b49d9e94ca18",
14
14
  "/memories/d3.min.js": "f2094bbf6141b359722c4fe454eb6c4b0f0e42cc10cc7af921fc158fceb86539",
15
15
  "/memories/graph-explorer.css": "ec128de59811f45c71b6bededd737989309205e903951eb8211ade7664f87486",
16
16
  "/memories/graph-explorer.js": "98bde3df25f42022b098d047d2c8e7a22b972c1d817a18a24a0ab075d77871d3",
17
17
  "/memories/memory-workspace.js": "013f6c8c72c3474768f8c374edccde0e3656fc243e56528e258c3c29acca64f5",
18
18
  "/memory-review.js": "f27fe519f7ad7eccc3b44124fae724aa51e2eb3cb060d2f1795600e0e41b074f",
19
- "/panes/cos-chat.css": "29a5d22d0f02d0707e65711d151ad7c8ac0ab4bfeb30ab04b1f9d9fcf7d457b8",
20
- "/panes/cos-chat.js": "0005562525cab6353ef8fc002d7960b30a8bf9fc9a9a1059c0123c6cf343366e",
19
+ "/panes/cos-chat.css": "079af47855641bf21aae75c34727f7c7be4357e0dde4ed78414d0e60776b71d4",
20
+ "/panes/cos-chat.js": "b94fca907500be21f777c5d5fa23b7415be48dcbd41d9fa30a4295ed1e3415d3",
21
21
  "/panes/dictation.js": "e9434b353a1a3ab3f21d4cef5b2d5f927a733f6af5f58285fda6a78472761f91",
22
- "/panes/meetings.css": "3ad4d0c5d59bd9f21c5ac9e234437cc0c81b381bd98ca4634b147efba5105a53",
23
- "/panes/meetings.js": "e3839cd7d52c98510ca232e41d3a336806d740b4a1381eab1a367d56a765f925",
22
+ "/panes/meetings.css": "635470e4d5f5665b046eab7382734c9772df569069c9b1eb10cfde6151014bfa",
23
+ "/panes/meetings.js": "e057f4dae697138111a4c07e8ec6c2a0502b5d93023cf491460bc7bf20172c85",
24
24
  "/panes/memories.css": "36731d6fd21ad1516dfeccf34fe5b2d3d267d21e40b47704055a0685334b579e",
25
- "/panes/memories.js": "28461f953ad0bf9d13287039594f2ce7ea29a46366e249240fa50f4b0f3b8c31",
26
- "/panes/messages.js": "be7a7b294f67a43fcb4cc518da987d2da2990484cb677ada810afa93af25a8a5",
25
+ "/panes/memories.js": "66341dd5126c40275979d1c40f8db72e1f8229ac9634a4bb72b70140db277f2a",
26
+ "/panes/messages.js": "7262ad82b6337a610b11ffff0677663e9317d1fe537f9e4161ae3af1ddef35ef",
27
27
  "/panes/preview.js": "79f1aecb27f48d584ed4cf37c7f4f423f0f9b45dd6c2fb24a9d88c72625d8680",
28
28
  "/panes/questions.css": "5c5a9b32ce32386b508ce326b17d40ecacbb0f2e049df7f4fe2a0009bd3b7908",
29
29
  "/panes/questions.js": "228e8cd0a9651a290cbb380db487af677a2fc7e93a64b1e33c9411cd8b013478",
30
30
  "/panes/reader-media.js": "4cf892a855aa2d0dce6978e69296cd49b8036d8fc62f12773b3fcadc210b2ef0",
31
31
  "/panes/sessions.css": "654e0509ab2b699b9ea92b6d9e2f7522146df1f0b6355915bef03caaf63addc4",
32
- "/panes/settings.js": "5c46c951fdcc2fc942486ec4b468b7e0bb0350a0a56a38d429948fc5147ab2f4",
32
+ "/panes/settings.js": "315458c5911f31592853800418dde6d87e9e61bbe96f90c77450c94f87d01951",
33
33
  "/panes/speakers.css": "49e70dae4266151b14b0057f2e43de169de225b8f3ac30343c81cf532735a487",
34
- "/panes/speakers.js": "8132c9244fb3965de802680840d2985978571988b9b4462b347d079330251bc5",
34
+ "/panes/speakers.js": "e4fbcc147c44f8bf798e0694e0c8b152d5cde82dc46823641c8e8d37714b8fba",
35
35
  "/panes/threads.js": "45fd7b56480be53225e91193cfa4c41a4ee7167890f10560e439bc7d2461747f",
36
36
  "/panes/today-actions.css": "c82f95b2aaf09802b15d47b24eccdb5c974afcab38ff5f861b3a8d8630b60869",
37
- "/panes/today-actions.js": "57ae8b378b1061060ae2a3f61d227d440d7806538085c1d5b310239c036e4bdc",
37
+ "/panes/today-actions.js": "c767b62b21a9f1debffd6e4ff4558891222b573535de1d78849b602030bb09bd",
38
38
  "/panes/work.css": "da50355dac690f712d1f6331f70ee5eadbf5982d37508648ae7786afc99ac7b1",
39
- "/panes/work.js": "1ef872b8c7e1a57bd7bd314e11eee7ac5fa9668871da7e644a1fbf5fd9c0d9e8",
40
- "/shell.js": "f1cc6a52c78e01ddba89ff27c6bd0e8f8bd8f29bdfcbb5150ee4ed66a8128d9d",
41
- "/theme.js": "360df6013cd638524543728f8fa3d35f5641d4d230da7c290de40c79216670ee",
39
+ "/panes/work.js": "913c56084a6df463cc83358ce360da30d529466f1c583683fd3a4fd5eafe2d30",
40
+ "/shell.js": "cc7d0d771712607d4ba49eb1cfc83c50c7fcae2203f1c386778fee43d1803a81",
41
+ "/theme.js": "e3e6422eaac43979d03149da114f5b83757b7361dd7b309cdf76be36a596044b",
42
42
  "/vendor/qrcode-encoder.js": "61d87cc7445cc66b862cff913de4571634746ea62bcd50d0f592be14f6d1ce46"
43
43
  }
44
44
  }
@@ -206,8 +206,9 @@ select.field {
206
206
 
207
207
  /* No bottom bar, on purpose. On a phone the browser owns the bottom edge (Safari's floating
208
208
  address pill, Comet's and Chrome's own bars), and iOS 26 WebKit stops drawing the page under
209
- its bar while any fixed element sits within ~60px of it. Navigation lives in the top bar
210
- (folded screen) or the left rail (unfolded screen and up). */
209
+ its bar while any fixed element sits within ~60px of it. Navigation lives in the top bar (phones),
210
+ the side rail at the right edge (a short screen under 600 px: the folded Duo, html.short-screen),
211
+ or the left rail (600 px and up). */
211
212
  .badge-dot { position: absolute; width: 7px; height: 7px; border-radius: 50%; background: var(--hot); }
212
213
 
213
214
  .rail { display: none; }
@@ -653,3 +654,58 @@ html { touch-action: manipulation; }
653
654
  .sx-te-choices, .ta-te-choices { display: flex; flex-wrap: wrap; gap: 8px; margin: 8px 0 6px; }
654
655
  .sx-te-choices .cx-btn, .ta-te-choices .cx-btn { flex: 1 1 12rem; min-height: 44px; white-space: normal; text-align: center; line-height: 1.25; }
655
656
  .sx-mac-q .sx-note { margin: 6px 0 0; }
657
+
658
+ /* ── 7.0.29: the folded Duo's side rail (Miles 21:39 "These side buttons are a must on the duo"; 21:42 tall phones keep
659
+ the top menu). theme.js sets html.short-screen from the screen's shape; this draws it only under 600 px on a touch
660
+ screen, so nothing changes from 600 px, on a mouse, or on a tall phone. It is the same nav.rail as the wide rail, so
661
+ the panes, their order, icons, badges and aria-current are one list: here it moves to the right edge as icons only,
662
+ with the top bar's Back at its foot on a reader. --rail-hit is 44 points on the phone: app.js railHitSync sets it from
663
+ the zoom (52 px at Safari's 85 percent Page Zoom, 44 px at 100); 52 px until it runs. Colours are the rail's own
664
+ tokens; the current pane keeps the wide rail's look (rail-active fill and its small shadow, no border). */
665
+ .rail-back { display: none; }
666
+ @media (max-width: 599px) and (pointer: coarse) {
667
+ html.short-screen { --rail-w: calc(var(--rail-hit, 52px) + 6px); }
668
+ html.short-screen .app-shell { display: flex; flex-direction: row; align-items: flex-start; }
669
+ html.short-screen.sx-frame .app-shell { flex-direction: row; align-items: stretch; }
670
+ html.short-screen .main-col { flex: 1 1 auto; min-width: 0; --safe-right: 0px; }
671
+ html.short-screen .rail {
672
+ order: 2; flex: none; width: calc(var(--rail-w) + var(--safe-right)); box-sizing: border-box;
673
+ display: flex; flex-direction: column; align-items: center; gap: 0;
674
+ position: sticky; top: 0; height: 100dvh; overflow-x: hidden; overflow-y: auto; overscroll-behavior: contain; scrollbar-width: none;
675
+ padding: calc(var(--safe-top) + 4px) var(--safe-right) calc(var(--safe-bottom) + 6px) 0;
676
+ background: var(--rail); border-left: 1px solid var(--rail-border);
677
+ }
678
+ html.short-screen.sx-frame .rail { position: static; height: auto; min-height: 0; }
679
+ html.short-screen .rail::-webkit-scrollbar { display: none; }
680
+ html.short-screen .rail-head, html.short-screen .rail-text, html.short-screen .rail-settings .rail-label, html.short-screen .server-line { display: none; }
681
+ html.short-screen .rail-btn, html.short-screen .rail-settings {
682
+ position: relative; flex: none; display: grid; place-items: center;
683
+ width: var(--rail-hit, 52px); height: var(--rail-hit, 52px); min-height: 0; padding: 0;
684
+ border-radius: 12px; color: var(--grey);
685
+ }
686
+ html.short-screen .rail-btn .icon, html.short-screen .rail-settings .icon { width: 22px; height: 22px; }
687
+ html.short-screen .rail-btn[aria-current='page'], html.short-screen .rail-settings[aria-current='page'] { background: var(--rail-active); color: var(--ink); box-shadow: 0 1px 2px rgba(43, 33, 26, 0.08); }
688
+ html.short-screen .rail-btn:active:not([aria-current='page']), html.short-screen .rail-settings:active:not([aria-current='page']) { background: var(--rail-border); color: var(--ink); }
689
+ html.short-screen .rail-btn .badge-dot { top: 9px; right: 9px; }
690
+ html.short-screen .rail-btn.hot::after { content: ''; position: absolute; top: 9px; right: 9px; width: 8px; height: 8px; border-radius: 50%; background: var(--hot); }
691
+ html.short-screen .rail-btn .inbox-count { position: absolute; top: 4px; right: 2px; }
692
+ html.short-screen .rail-btn .inbox-dot { position: absolute; top: 9px; right: 9px; }
693
+ html.short-screen .rail-sep { flex: none; width: 24px; height: 1px; margin: 3px 0; background: var(--rail-border); }
694
+ html.short-screen .rail-spacer { display: none; }
695
+ /* Back: alone at the foot, outlined so it never reads as a pane; it stays on screen when the panes scroll. */
696
+ html.short-screen .rail-back:not([hidden]) {
697
+ display: grid; place-items: center; flex: none; margin-top: auto;
698
+ position: sticky; bottom: 0; z-index: 1;
699
+ width: var(--rail-hit, 52px); height: var(--rail-hit, 52px);
700
+ border: 1px solid var(--btn-border); border-radius: 12px; background: var(--rail); color: var(--ink);
701
+ }
702
+ html.short-screen .rail-back .icon { width: 22px; height: 22px; stroke-width: 2; }
703
+ html.short-screen .rail-back:active { background: var(--rail-border); }
704
+ /* The focus ring sits inside the button: the rail clips anything past its edge. */
705
+ html.short-screen .rail :focus-visible { outline: 2px solid var(--accent); outline-offset: -4px; border-radius: 12px; }
706
+ /* One way to switch panes on a screen: the rail. On a list the top bar keeps the pane's name, without the caret and
707
+ its menu (app.js makes the button inert); on a reader its Back moves to the rail and the left of the top bar is
708
+ empty. */
709
+ html.short-screen .section-btn .caret, html.short-screen #topbar-back { display: none !important; }
710
+ html.short-screen .section-btn { cursor: default; }
711
+ }