instar 1.3.1596 → 1.3.1598

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 (107) hide show
  1. package/dashboard/desktop.css +8 -0
  2. package/dashboard/desktop.html +10 -0
  3. package/dashboard/desktop.js +98 -1
  4. package/dist/commands/server.d.ts.map +1 -1
  5. package/dist/commands/server.js +10 -7
  6. package/dist/commands/server.js.map +1 -1
  7. package/dist/core/DesktopAccessAudit.d.ts +3 -1
  8. package/dist/core/DesktopAccessAudit.d.ts.map +1 -1
  9. package/dist/core/DesktopAccessAudit.js +1 -0
  10. package/dist/core/DesktopAccessAudit.js.map +1 -1
  11. package/dist/core/DesktopAead.d.ts +8 -1
  12. package/dist/core/DesktopAead.d.ts.map +1 -1
  13. package/dist/core/DesktopAead.js +18 -1
  14. package/dist/core/DesktopAead.js.map +1 -1
  15. package/dist/core/DesktopHost.d.ts +26 -1
  16. package/dist/core/DesktopHost.d.ts.map +1 -1
  17. package/dist/core/DesktopHost.js.map +1 -1
  18. package/dist/core/DesktopLockState.d.ts +38 -0
  19. package/dist/core/DesktopLockState.d.ts.map +1 -0
  20. package/dist/core/DesktopLockState.js +43 -0
  21. package/dist/core/DesktopLockState.js.map +1 -0
  22. package/dist/core/DesktopLockedController.d.ts +138 -0
  23. package/dist/core/DesktopLockedController.d.ts.map +1 -0
  24. package/dist/core/DesktopLockedController.js +442 -0
  25. package/dist/core/DesktopLockedController.js.map +1 -0
  26. package/dist/core/DesktopOperatorAssertion.d.ts +39 -0
  27. package/dist/core/DesktopOperatorAssertion.d.ts.map +1 -1
  28. package/dist/core/DesktopOperatorAssertion.js +49 -0
  29. package/dist/core/DesktopOperatorAssertion.js.map +1 -1
  30. package/dist/core/DesktopProvenVersions.d.ts +19 -0
  31. package/dist/core/DesktopProvenVersions.d.ts.map +1 -0
  32. package/dist/core/DesktopProvenVersions.js +8 -0
  33. package/dist/core/DesktopProvenVersions.js.map +1 -0
  34. package/dist/core/DirectDesktopAccess.d.ts +20 -1
  35. package/dist/core/DirectDesktopAccess.d.ts.map +1 -1
  36. package/dist/core/DirectDesktopAccess.js +263 -10
  37. package/dist/core/DirectDesktopAccess.js.map +1 -1
  38. package/dist/core/MacDesktopHost.d.ts +32 -1
  39. package/dist/core/MacDesktopHost.d.ts.map +1 -1
  40. package/dist/core/MacDesktopHost.js +115 -37
  41. package/dist/core/MacDesktopHost.js.map +1 -1
  42. package/dist/core/MultiMachineCoordinator.d.ts +11 -2
  43. package/dist/core/MultiMachineCoordinator.d.ts.map +1 -1
  44. package/dist/core/MultiMachineCoordinator.js +15 -4
  45. package/dist/core/MultiMachineCoordinator.js.map +1 -1
  46. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  47. package/dist/core/PostUpdateMigrator.js +12 -1
  48. package/dist/core/PostUpdateMigrator.js.map +1 -1
  49. package/dist/core/WriteDomainRegistry.d.ts.map +1 -1
  50. package/dist/core/WriteDomainRegistry.js +8 -0
  51. package/dist/core/WriteDomainRegistry.js.map +1 -1
  52. package/dist/core/types.d.ts +6 -0
  53. package/dist/core/types.d.ts.map +1 -1
  54. package/dist/core/types.js.map +1 -1
  55. package/dist/data/standards-guard-index.json +1 -1
  56. package/dist/data/standards-guard-index.meta.json +2 -2
  57. package/dist/data/standards-registry.meta.json +1 -1
  58. package/dist/messaging/telegram-origin/OriginAwareness.d.ts +4 -0
  59. package/dist/messaging/telegram-origin/OriginAwareness.d.ts.map +1 -1
  60. package/dist/messaging/telegram-origin/OriginAwareness.js +7 -1
  61. package/dist/messaging/telegram-origin/OriginAwareness.js.map +1 -1
  62. package/dist/native/instar-desktop-host +0 -0
  63. package/dist/native/instar-desktop-host.json +1 -1
  64. package/dist/scaffold/templates.d.ts.map +1 -1
  65. package/dist/scaffold/templates.js +1 -1
  66. package/dist/scaffold/templates.js.map +1 -1
  67. package/dist/server/AgentServer.d.ts +4 -0
  68. package/dist/server/AgentServer.d.ts.map +1 -1
  69. package/dist/server/AgentServer.js +116 -3
  70. package/dist/server/AgentServer.js.map +1 -1
  71. package/dist/server/CapabilityIndex.d.ts.map +1 -1
  72. package/dist/server/CapabilityIndex.js +2 -1
  73. package/dist/server/CapabilityIndex.js.map +1 -1
  74. package/dist/server/DesktopMeshBroker.d.ts +6 -0
  75. package/dist/server/DesktopMeshBroker.d.ts.map +1 -1
  76. package/dist/server/DesktopMeshBroker.js +17 -1
  77. package/dist/server/DesktopMeshBroker.js.map +1 -1
  78. package/dist/server/DesktopStreamManager.d.ts +11 -3
  79. package/dist/server/DesktopStreamManager.d.ts.map +1 -1
  80. package/dist/server/DesktopStreamManager.js +24 -1
  81. package/dist/server/DesktopStreamManager.js.map +1 -1
  82. package/dist/server/leaseRenewalDependencies.d.ts +15 -0
  83. package/dist/server/leaseRenewalDependencies.d.ts.map +1 -0
  84. package/dist/server/leaseRenewalDependencies.js +10 -0
  85. package/dist/server/leaseRenewalDependencies.js.map +1 -0
  86. package/dist/server/machineRoutes.d.ts +1 -0
  87. package/dist/server/machineRoutes.d.ts.map +1 -1
  88. package/dist/server/machineRoutes.js +14 -0
  89. package/dist/server/machineRoutes.js.map +1 -1
  90. package/dist/server/routes.d.ts +9 -0
  91. package/dist/server/routes.d.ts.map +1 -1
  92. package/dist/server/routes.js +99 -0
  93. package/dist/server/routes.js.map +1 -1
  94. package/dist/testing/selfActionRegistry.d.ts.map +1 -1
  95. package/dist/testing/selfActionRegistry.js +58 -0
  96. package/dist/testing/selfActionRegistry.js.map +1 -1
  97. package/package.json +1 -1
  98. package/scripts/build-native-desktop-host.mjs +4 -2
  99. package/src/data/builtin-manifest.json +66 -66
  100. package/src/data/standards-guard-index.json +1 -1
  101. package/src/data/standards-guard-index.meta.json +2 -2
  102. package/src/data/standards-registry.meta.json +1 -1
  103. package/src/scaffold/templates.ts +1 -1
  104. package/upgrades/1.3.1597.md +20 -0
  105. package/upgrades/1.3.1598.md +24 -0
  106. package/upgrades/side-effects/app-conversations-lease-renewal.md +51 -0
  107. package/upgrades/side-effects/desktop-locked-screen.md +129 -0
@@ -41,3 +41,11 @@ body.desktop-live #localCursor{display:block}
41
41
  .setup-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
42
42
  .setup-note:empty { display: none; }
43
43
  .setup-quiet { background: none; border: none; color: inherit; opacity: 0.7; text-decoration: underline; }
44
+
45
+ #lockedPassword{position:absolute;z-index:8;left:max(.5rem,env(safe-area-inset-left));right:max(.5rem,env(safe-area-inset-right));top:max(.5rem,env(safe-area-inset-top));display:grid;gap:.4rem;padding:.6rem;background:#111827f2;border:1px solid #64748b;border-radius:10px;color:#f1f5f9}
46
+ #lockedPassword[hidden],#leaveUnlockedButton[hidden]{display:none}
47
+ #lockedPassword input{width:100%;font-size:16px;padding:.55rem}
48
+ #lockedPassword p{margin:0;font-size:.8rem}
49
+ #leaveUnlockedButton{position:absolute;z-index:8;right:max(.5rem,env(safe-area-inset-right));bottom:4rem;padding:.55rem .8rem}
50
+ #lockedCard{margin-top:.8rem}
51
+ #lockedCard[hidden]{display:none}
@@ -19,6 +19,10 @@
19
19
  <label>Machine <select id="machine"></select></label>
20
20
  <button id="requestButton">Open selected machine</button>
21
21
  <ul id="machineNotes" class="machine-notes" aria-label="Machines that are not ready"></ul>
22
+ <div id="lockedCard" hidden>
23
+ <p id="lockedCardText"></p>
24
+ <button id="lockedToggle" type="button" hidden></button>
25
+ </div>
22
26
  </section>
23
27
  <section id="setup" hidden>
24
28
  <h2>Machines</h2>
@@ -86,6 +90,12 @@
86
90
  <button data-remote-key="F11" type="button">F11</button>
87
91
  <button data-remote-key="F12" type="button">F12</button>
88
92
  </div>
93
+ <form id="lockedPassword" hidden>
94
+ <label>Mac password <input id="lockedPasswordInput" type="password" autocomplete="off" autocapitalize="off" autocorrect="off" spellcheck="false" maxlength="256"></label>
95
+ <button type="submit">Unlock</button>
96
+ <p>The machine serving this page and the tunnel provider can read what you type here. The Instar app is safer.</p>
97
+ </form>
98
+ <button id="leaveUnlockedButton" type="button" hidden>Leave unlocked</button>
89
99
  <div id="keyboardBar" hidden>
90
100
  <label class="type-label" for="typeText">Type on remote desktop</label>
91
101
  <input id="typeText" maxlength="64" autocomplete="off" autocapitalize="off" autocorrect="off" spellcheck="false" enterkeyhint="done">
@@ -251,7 +251,7 @@ const base64ToBytes = text => Uint8Array.from(atob(text), character => character
251
251
  function recordIv(direction, sequence) {
252
252
  const iv = new Uint8Array(12);
253
253
  const view = new DataView(iv.buffer);
254
- view.setUint32(0, direction === 'frame' ? 1 : 2);
254
+ view.setUint32(0, { frame: 1, input: 2, control: 3 }[direction]);
255
255
  view.setBigUint64(4, sequence);
256
256
  return iv;
257
257
  }
@@ -410,11 +410,65 @@ function receiveH264(bytes, epoch) {
410
410
  timestamp, data }));
411
411
  needsVideoKeyframe = false;
412
412
  }
413
+ const CONTROL_MARKER = 0xc7;
414
+ let controlSequence = 0n;
415
+ let lockedSealKey = '';
416
+ let lockedScreenState = '';
417
+ let lockedBoxAllowed = false;
418
+ let secretSequence = 0;
419
+ function syncLockedPassword() {
420
+ const show = lockedBoxAllowed && lockedScreenState === 'locked-in-session' && !!lockedSealKey;
421
+ $('lockedPassword').hidden = !show;
422
+ if (!show) $('lockedPasswordInput').value = '';
423
+ }
424
+ function lockedMessage(reason) {
425
+ return { 'relock-pending': 'This Mac is finishing a relock. Try again in a couple of minutes.',
426
+ 'attempts-per-request': 'Too many tries in this session.', 'attempts-per-day': 'Too many tries today.',
427
+ 'seal-stale': 'The connection changed. Type your password again.',
428
+ aborted: 'Stopped before the password finished. Type it again.',
429
+ 'field-dirty': 'Clearing the password field. Try again in a moment.',
430
+ 'browser-box-off': 'Password entry from a browser is off for this Mac.',
431
+ 'tunnel-not-first-tier': 'Password entry is off while the backup connection is in use.' }[reason]
432
+ ?? 'This Mac did not accept that. Try again.';
433
+ }
434
+ async function receiveControl(record) {
435
+ const view = new DataView(record.buffer, record.byteOffset, record.byteLength);
436
+ const sequence = view.getBigUint64(0) & 0x00ff_ffff_ffff_ffffn;
437
+ if (sequence <= controlSequence) throw new Error('Remote control record invalid.');
438
+ const plain = await crypto.subtle.decrypt({ name: 'AES-GCM', iv: recordIv('control', sequence),
439
+ additionalData: recordAad('control') }, aesKey, record.subarray(8));
440
+ controlSequence = sequence;
441
+ const value = JSON.parse(new TextDecoder().decode(plain));
442
+ if (value.type === 'seal') lockedSealKey = value.key;
443
+ if (value.type === 'state') { lockedScreenState = value.state; lockedBoxAllowed = value.box === true; }
444
+ if (value.type === 'secret-result' && value.refused) status(lockedMessage(value.refused));
445
+ if (value.type === 'secret-result' && value.sent) { $('leaveUnlockedButton').hidden = false; }
446
+ if (value.type === 'relock') status(value.phase === 'left-unlocked' ? 'This Mac will stay unlocked.' : 'Relocking has begun.');
447
+ syncLockedPassword();
448
+ }
449
+ /** Sealed for the helper alone: Node and the front carry bytes they cannot read. */
450
+ async function sendLockedPassword(text) {
451
+ const helperKey = lockedSealKey;
452
+ const pair = await crypto.subtle.generateKey({ name: 'ECDH', namedCurve: 'P-256' }, true, ['deriveBits']);
453
+ const helper = await crypto.subtle.importKey('raw', base64ToBytes(helperKey), { name: 'ECDH', namedCurve: 'P-256' }, false, []);
454
+ const bits = await crypto.subtle.deriveBits({ name: 'ECDH', public: helper }, pair.privateKey, 256);
455
+ const hkdf = await crypto.subtle.importKey('raw', bits, 'HKDF', false, ['deriveKey']);
456
+ const key = await crypto.subtle.deriveKey({ name: 'HKDF', hash: 'SHA-256', salt: new Uint8Array(0),
457
+ info: encoder.encode(`instar-desktop-secret-v1:${requestId}:${helperKey}`) }, hkdf,
458
+ { name: 'AES-GCM', length: 256 }, false, ['encrypt']);
459
+ const seq = ++secretSequence;
460
+ const iv = crypto.getRandomValues(new Uint8Array(12));
461
+ const ct = await crypto.subtle.encrypt({ name: 'AES-GCM', iv,
462
+ additionalData: encoder.encode(JSON.stringify(['instar-desktop-secret-v1', requestId, seq])) }, key, encoder.encode(text));
463
+ const ephemeral = await crypto.subtle.exportKey('raw', pair.publicKey);
464
+ return sendCommand({ type: 'secret', secret: { ephemeral: bytesToBase64(ephemeral), iv: bytesToBase64(iv), ct: bytesToBase64(ct), seq } });
465
+ }
413
466
  async function receiveFrame(recordBuffer, epoch) {
414
467
  if (!inputEnabled || epoch !== streamEpoch) return;
415
468
  if (!aesKey || !(recordBuffer instanceof ArrayBuffer) || recordBuffer.byteLength < 28
416
469
  || recordBuffer.byteLength > 1_600_000) throw new Error('Encrypted frame invalid.');
417
470
  const record = new Uint8Array(recordBuffer);
471
+ if (record[0] === CONTROL_MARKER) { await receiveControl(record); return; }
418
472
  const sequence = new DataView(recordBuffer).getBigUint64(0);
419
473
  // The target may drop an already-encrypted frame under backpressure. A
420
474
  // strictly increasing nonce still rejects replay/reordering without treating
@@ -699,6 +753,7 @@ async function loadTargetsAndMaybeOpen({ open = true } = {}) {
699
753
  const trustedKey = typeof target.targetSigningPublicKey === 'string'
700
754
  ? target.targetSigningPublicKey : '';
701
755
  option.disabled = !target.ready || !trustedKey;
756
+ setTimeout(() => { void refreshLockedCard(); }, 0);
702
757
  option.textContent = option.disabled
703
758
  ? `${target.label} — ${target.unavailableReason || 'Unavailable right now'}`
704
759
  : target.label;
@@ -817,6 +872,8 @@ async function requestDesktop() {
817
872
  browserEcdhPublicKey: bytesToBase64(ecdhPublicKey),
818
873
  browserNonce: crypto.randomUUID().replaceAll('-', ''),
819
874
  videoCodec,
875
+ client: 'browser',
876
+ controlRecords: true,
820
877
  });
821
878
  requestId = result.requestId;
822
879
  const approved = await api(`/desktop/requests/${encodeURIComponent(requestId)}`);
@@ -875,6 +932,10 @@ async function openStream(result, resume = null) {
875
932
  sendChain = Promise.resolve();
876
933
  receiveChain = Promise.resolve();
877
934
  socket.binaryType = 'arraybuffer';
935
+ controlSequence = 0n;
936
+ lockedSealKey = '';
937
+ secretSequence = 0;
938
+ syncLockedPassword();
878
939
  return new Promise((resolve, reject) => {
879
940
  let opened = false;
880
941
  let settled = false;
@@ -930,8 +991,17 @@ async function openStream(result, resume = null) {
930
991
  });
931
992
  }
932
993
 
994
+ const LOCK_SCREEN_KEYS = new Set(['Enter', 'Tab', 'Escape', 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown']);
995
+ /** A password typed at the shield must never leave as plain input. The helper
996
+ * also refuses it, but only this stops it reaching the server at all. */
997
+ function refusedAtLock(action) {
998
+ if (!['locked-in-session', 'locked-by-policy', 'screensaver-or-sleep', 'unknown'].includes(lockedScreenState)) return false;
999
+ if (action.kind === 'type') return true;
1000
+ return action.kind === 'keyDown' && !LOCK_SCREEN_KEYS.has(action.key);
1001
+ }
933
1002
  function send(action) {
934
1003
  if (!inputEnabled) return Promise.resolve(false);
1004
+ if (refusedAtLock(action)) return Promise.resolve(true);
935
1005
  if (screenSwitch && Date.now() - screenSwitch.at >= SCREEN_SWITCH_LIMIT_MS) screenSwitch = null;
936
1006
  if (screenSwitch && ['click', 'buttonDown', 'move'].includes(action.kind)) return Promise.resolve(false);
937
1007
  return sendCommand({ type: 'input', action });
@@ -1559,6 +1629,33 @@ $('stopButton').addEventListener('click', async () => {
1559
1629
  catch { message = 'The viewer closed, but the target has not confirmed revocation. Access expires automatically.'; }
1560
1630
  leave(message);
1561
1631
  });
1632
+ $('lockedPassword')?.addEventListener('submit', event => {
1633
+ event.preventDefault();
1634
+ const input = $('lockedPasswordInput');
1635
+ const text = input.value;
1636
+ input.value = '';
1637
+ if (text) void sendLockedPassword(text);
1638
+ });
1639
+ const LOCKED_REASONS = { 'unsupported-macos': 'Not available yet: this Mac\u2019s macOS has not passed the crash and Stop tests.' };
1640
+ async function refreshLockedCard() {
1641
+ let rows;
1642
+ try { rows = (await api('/desktop/locked/status?scope=pool')).machines; }
1643
+ catch { $('lockedCard').hidden = true; return; }
1644
+ const row = rows?.find(item => item.machineId === $('machine').value);
1645
+ if (!row) { $('lockedCard').hidden = true; return; }
1646
+ $('lockedCard').hidden = !row.enabled && !!row.refusal;
1647
+ $('lockedCardText').textContent = row.refusal ? LOCKED_REASONS[row.refusal] ?? 'Opening this Mac at its lock screen is unavailable.'
1648
+ : row.enabled ? 'You can open this Mac at its lock screen. It relocks when you finish.' : 'Opening this Mac at its lock screen is off.';
1649
+ $('lockedToggle').hidden = !!row.refusal;
1650
+ $('lockedToggle').textContent = row.enabled ? 'Turn off' : 'Turn on';
1651
+ $('lockedToggle').onclick = async () => {
1652
+ try { await api('/desktop/locked/settings', 'POST', { machineId: row.machineId, enabled: !row.enabled }); }
1653
+ catch (error) { status(error.message); }
1654
+ void refreshLockedCard();
1655
+ };
1656
+ }
1657
+ $('machine')?.addEventListener('change', () => { void refreshLockedCard(); });
1658
+ $('leaveUnlockedButton')?.addEventListener('click', () => { void sendCommand({ type: 'leave-unlocked' }); });
1562
1659
  window.addEventListener('beforeunload', () => {
1563
1660
  stopping = true;
1564
1661
  sendCommand({ type: 'stop' });
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"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"}
@@ -4442,16 +4442,19 @@ export async function startServer(options) {
4442
4442
  onLeasePullTick: () => _leaseHandbackReconciler?.observe(),
4443
4443
  });
4444
4444
  const machineRole = coordinator.start();
4445
- // The origin writer below requires a current lease even on ordinary,
4446
- // non-development installations. Enroll its renewal carrier before lease
4447
- // initialization; this does not bypass explicit opt-outs or lease fences.
4448
- const originLeaseRenewal = config.messaging.some(m => m.type === 'telegram' && m.enabled)
4449
- ? coordinator.enrollOriginWriterLeaseRenewal() : undefined;
4445
+ // The origin writer and native app conversations require a current lease even
4446
+ // on ordinary, non-development installations (without Telegram, the app was
4447
+ // the only surface and its lease lapsed at TTL between 2-minute heartbeats).
4448
+ // Enroll the renewal carrier before lease initialization; this does not bypass
4449
+ // explicit opt-outs or lease fences.
4450
+ const { leaseRenewalDependencies } = await import('../server/leaseRenewalDependencies.js');
4451
+ const leaseRenewalReleases = leaseRenewalDependencies(config).map((name) => coordinator.enrollLeaseRenewalDependency(name));
4450
4452
  const releaseOriginLeaseRenewal = () => {
4451
- originLeaseRenewal?.();
4453
+ for (const release of leaseRenewalReleases.splice(0))
4454
+ release();
4452
4455
  process.removeListener('exit', releaseOriginLeaseRenewal);
4453
4456
  };
4454
- if (originLeaseRenewal)
4457
+ if (leaseRenewalReleases.length > 0)
4455
4458
  process.once('exit', releaseOriginLeaseRenewal);
4456
4459
  if (coordinator.enabled) {
4457
4460
  console.log(pc.green(` Multi-machine: ${pc.bold(machineRole)} (${coordinator.identity.machineId.slice(0, 12)}...)`));