@hasna/recordings 0.2.10 → 0.2.13

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 (167) hide show
  1. package/README.md +350 -35
  2. package/bun.lock +3 -0
  3. package/dist/__tests__/helpers/native-fs-guard.d.ts +2 -0
  4. package/dist/__tests__/helpers/native-fs-guard.d.ts.map +1 -0
  5. package/dist/cli/index.js +1728 -320
  6. package/dist/cli/macos-permissions.d.ts +13 -0
  7. package/dist/cli/macos-permissions.d.ts.map +1 -0
  8. package/dist/cli/options.d.ts +12 -0
  9. package/dist/cli/options.d.ts.map +1 -1
  10. package/dist/db/pg-migrations.d.ts.map +1 -1
  11. package/dist/db/recordings.d.ts +2 -1
  12. package/dist/db/recordings.d.ts.map +1 -1
  13. package/dist/db/remote-storage.d.ts +5 -1
  14. package/dist/db/remote-storage.d.ts.map +1 -1
  15. package/dist/index.d.ts +2 -2
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +628 -147
  18. package/dist/lib/bun-runtime.d.ts +11 -0
  19. package/dist/lib/bun-runtime.d.ts.map +1 -0
  20. package/dist/lib/config.d.ts +8 -1
  21. package/dist/lib/config.d.ts.map +1 -1
  22. package/dist/lib/enhancer.d.ts +10 -2
  23. package/dist/lib/enhancer.d.ts.map +1 -1
  24. package/dist/lib/install-maintenance.d.ts +24 -0
  25. package/dist/lib/install-maintenance.d.ts.map +1 -0
  26. package/dist/lib/machine.d.ts +2 -0
  27. package/dist/lib/machine.d.ts.map +1 -0
  28. package/dist/lib/recorder.d.ts +2 -1
  29. package/dist/lib/recorder.d.ts.map +1 -1
  30. package/dist/lib/recording-create-identity.d.ts +13 -0
  31. package/dist/lib/recording-create-identity.d.ts.map +1 -0
  32. package/dist/lib/release-install-policy.d.ts +31 -0
  33. package/dist/lib/release-install-policy.d.ts.map +1 -0
  34. package/dist/lib/transcriber.d.ts.map +1 -1
  35. package/dist/mcp/index.d.ts.map +1 -1
  36. package/dist/mcp/index.js +977 -194
  37. package/dist/sdk/index.js +5 -1
  38. package/dist/sdk/v1.generated.d.ts +4 -0
  39. package/dist/sdk/v1.generated.d.ts.map +1 -1
  40. package/dist/server/cloud-config.d.ts +9 -0
  41. package/dist/server/cloud-config.d.ts.map +1 -0
  42. package/dist/server/cloud-readiness.d.ts +10 -0
  43. package/dist/server/cloud-readiness.d.ts.map +1 -0
  44. package/dist/server/cloud.d.ts +9 -18
  45. package/dist/server/cloud.d.ts.map +1 -1
  46. package/dist/server/index.js +1654 -304
  47. package/dist/server/migrate-command.d.ts +11 -0
  48. package/dist/server/migrate-command.d.ts.map +1 -0
  49. package/dist/server/openapi.d.ts +22 -0
  50. package/dist/server/openapi.d.ts.map +1 -1
  51. package/dist/server/repo.d.ts +8 -1
  52. package/dist/server/repo.d.ts.map +1 -1
  53. package/dist/server/serve.d.ts +6 -1
  54. package/dist/server/serve.d.ts.map +1 -1
  55. package/dist/server/v1.d.ts.map +1 -1
  56. package/dist/storage.js +515 -80
  57. package/dist/store.d.ts +3 -1
  58. package/dist/store.d.ts.map +1 -1
  59. package/dist/types/index.d.ts +10 -0
  60. package/dist/types/index.d.ts.map +1 -1
  61. package/dist/version.d.ts +1 -1
  62. package/package.json +7 -4
  63. package/packaging/macos/Empty.entitlements +5 -0
  64. package/packaging/macos/Library/LaunchDaemons/com.hasna.recordings.updater.plist +34 -0
  65. package/packaging/macos/Verifier.entitlements +10 -0
  66. package/packaging/macos/artifact-verifier.sb +33 -0
  67. package/packaging/macos/build_release_pkg.sh +872 -0
  68. package/packaging/macos/managed_bootstrap.sh +623 -0
  69. package/packaging/macos/pkgutil_fingerprint.awk +37 -0
  70. package/packaging/macos/release_lifecycle.ts +463 -0
  71. package/packaging/macos/scripts/postinstall +565 -0
  72. package/packaging/macos/scripts/preinstall +297 -0
  73. package/scripts/build_companion_cli.sh +337 -0
  74. package/scripts/build_native_fs_guard.sh +59 -0
  75. package/scripts/generate-sdk.ts +19 -1
  76. package/scripts/install_macos_app.sh +1922 -77
  77. package/scripts/macos_artifact.ts +5316 -0
  78. package/scripts/migrate.ts +38 -10
  79. package/scripts/native/prebuilds/darwin-universal/recordings_fs_guard.node +0 -0
  80. package/scripts/native/recordings_fs_guard.c +1167 -0
  81. package/scripts/native_fs_guard.ts +158 -0
  82. package/scripts/release-guard.ts +20 -1
  83. package/scripts/resolve_tailscale_cli.sh +389 -0
  84. package/scripts/smoke_macos_app.sh +573 -0
  85. package/src/native/Recordings/App/ContentView.swift +83 -0
  86. package/src/native/Recordings/App/MenuBarStatusView.swift +102 -0
  87. package/src/native/Recordings/App/RecordWorkspaceView.swift +468 -0
  88. package/src/native/Recordings/App/RecordingDetailView.swift +138 -0
  89. package/src/native/Recordings/App/RecordingsApp.swift +367 -44
  90. package/src/native/Recordings/App/RecordingsListView.swift +130 -0
  91. package/src/native/Recordings/App/RecordingsStore.swift +206 -0
  92. package/src/native/Recordings/App/RuntimeSmoke.swift +158 -0
  93. package/src/native/Recordings/App/SidebarView.swift +212 -0
  94. package/src/native/Recordings/App/Theme.swift +87 -0
  95. package/src/native/Recordings/Package.resolved +5 -5
  96. package/src/native/Recordings/Package.swift +62 -2
  97. package/src/native/Recordings/RecordingsLib/AccessibilityPromptGate.swift +104 -0
  98. package/src/native/Recordings/RecordingsLib/BrandAssets.swift +0 -32
  99. package/src/native/Recordings/RecordingsLib/ChromeSurface.swift +17 -0
  100. package/src/native/Recordings/RecordingsLib/Info.plist +7 -3
  101. package/src/native/Recordings/RecordingsLib/MenuBarPresentation.swift +37 -0
  102. package/src/native/Recordings/RecordingsLib/NativeAppDiagnostics.swift +32 -2
  103. package/src/native/Recordings/RecordingsLib/NativeMachineIdentity.swift +24 -0
  104. package/src/native/Recordings/RecordingsLib/NativePCMRecorder.swift +262 -23
  105. package/src/native/Recordings/RecordingsLib/OpenAIAPIKeyStore.swift +56 -0
  106. package/src/native/Recordings/RecordingsLib/PermissionRequestLaunchPlan.swift +56 -0
  107. package/src/native/Recordings/RecordingsLib/ProjectStore.swift +321 -25
  108. package/src/native/Recordings/RecordingsLib/RealtimeTranscriptionClient.swift +605 -90
  109. package/src/native/Recordings/RecordingsLib/Recording.swift +141 -0
  110. package/src/native/Recordings/RecordingsLib/RecordingEngine.swift +3704 -376
  111. package/src/native/Recordings/RecordingsLib/RecordingStartControlPresentation.swift +29 -0
  112. package/src/native/Recordings/RecordingsLib/RecordingsCLI.entitlements +10 -0
  113. package/src/native/Recordings/RecordingsLib/RecordingsCLI.swift +222 -0
  114. package/src/native/Recordings/RecordingsLib/SettingsView.swift +45 -11
  115. package/src/native/Recordings/RecordingsLib/SpeechIntent.swift +331 -0
  116. package/src/native/Recordings/RecordingsLib/SpeechIntentClassifier.swift +232 -0
  117. package/src/native/Recordings/RecordingsLib/VoiceShortcuts.swift +17 -7
  118. package/src/native/Recordings/RecordingsTests/CLIRunnerTests.swift +1343 -0
  119. package/src/native/Recordings/RecordingsTests/MenuBarPresentationTests.swift +98 -0
  120. package/src/native/Recordings/RecordingsTests/NativeAppDiagnosticsTests.swift +20 -0
  121. package/src/native/Recordings/RecordingsTests/NativePCMRecorderTests.swift +370 -1
  122. package/src/native/Recordings/RecordingsTests/PasteTargetTests.swift +987 -1
  123. package/src/native/Recordings/RecordingsTests/ProjectStoreTests.swift +385 -0
  124. package/src/native/Recordings/RecordingsTests/RealtimeTranscriptionTests.swift +835 -8
  125. package/src/native/Recordings/RecordingsTests/RecordingBridgeTests.swift +180 -0
  126. package/src/native/Recordings/RecordingsTests/RecordingEngineDeliveryTests.swift +760 -0
  127. package/src/native/Recordings/RecordingsTests/RecordingStartControlPresentationTests.swift +42 -0
  128. package/src/native/Recordings/RecordingsTests/RecordingStartGateTests.swift +264 -0
  129. package/src/native/Recordings/RecordingsTests/RecordingStartTimingTests.swift +129 -0
  130. package/src/native/Recordings/RecordingsTests/SpeechIntentTests.swift +600 -0
  131. package/src/native/Recordings/RecordingsTests/TranscriptResolutionTests.swift +62 -1
  132. package/src/native/Recordings/RecordingsTests/TranscriptionResultIdentityTests.swift +21 -0
  133. package/src/native/Recordings/Updater/BootstrapPreflight/BootstrapPreflightMain.swift +739 -0
  134. package/src/native/Recordings/Updater/Broker/ActivationRecoveryPolicy.swift +254 -0
  135. package/src/native/Recordings/Updater/Broker/ApplicationNamespace.swift +463 -0
  136. package/src/native/Recordings/Updater/Broker/ApplicationProcessQuiescence.swift +55 -0
  137. package/src/native/Recordings/Updater/Broker/ArtifactIngest.swift +341 -0
  138. package/src/native/Recordings/Updater/Broker/AtomicActivation.swift +477 -0
  139. package/src/native/Recordings/Updater/Broker/BrokerMain.swift +624 -0
  140. package/src/native/Recordings/Updater/Broker/CanonicalTreeCopy.swift +63 -0
  141. package/src/native/Recordings/Updater/Broker/CodeValidation.swift +561 -0
  142. package/src/native/Recordings/Updater/Broker/DarwinACLValidator.swift +66 -0
  143. package/src/native/Recordings/Updater/Broker/HostOSProductVersion.swift +50 -0
  144. package/src/native/Recordings/Updater/Broker/InstallJournal.swift +362 -0
  145. package/src/native/Recordings/Updater/Broker/InstallRecovery.swift +662 -0
  146. package/src/native/Recordings/Updater/Broker/MonotonicState.swift +456 -0
  147. package/src/native/Recordings/Updater/Broker/PeerIdentity.swift +274 -0
  148. package/src/native/Recordings/Updater/Broker/VerifierRunner.swift +81 -0
  149. package/src/native/Recordings/Updater/BrokerTests/ActivationRecoveryPolicyTests.swift +259 -0
  150. package/src/native/Recordings/Updater/BrokerTests/CanonicalReleaseOrderTests.swift +21 -0
  151. package/src/native/Recordings/Updater/Client/ClientMain.swift +119 -0
  152. package/src/native/Recordings/Updater/Protocol/BoundedProcess.swift +159 -0
  153. package/src/native/Recordings/Updater/Protocol/CandidateMetadataPolicy.swift +214 -0
  154. package/src/native/Recordings/Updater/Protocol/HostOSVersionPolicy.swift +55 -0
  155. package/src/native/Recordings/Updater/Protocol/MonotonicReleasePolicy.swift +152 -0
  156. package/src/native/Recordings/Updater/Protocol/ReleaseEnvelope.swift +229 -0
  157. package/src/native/Recordings/Updater/Protocol/UpdateProtocol.swift +119 -0
  158. package/src/native/Recordings/Updater/ProtocolTests/BoundedProcessRunnerTests.swift +60 -0
  159. package/src/native/Recordings/Updater/ProtocolTests/CandidateMetadataPolicyTests.swift +168 -0
  160. package/src/native/Recordings/Updater/ProtocolTests/HostOSVersionPolicyTests.swift +62 -0
  161. package/src/native/Recordings/Updater/ProtocolTests/MonotonicReleasePolicyTests.swift +172 -0
  162. package/src/native/Recordings/Updater/ProtocolTests/ReleaseEnvelopeValidationTests.swift +65 -0
  163. package/src/native/Recordings/Updater/Signer/SignerMain.swift +210 -0
  164. package/src/native/Recordings/Updater/VerifierLauncher/RecordingsVerifierLauncher.c +725 -0
  165. package/src/native/Recordings/Updater/VerifierLauncher/include/RecordingsVerifierLauncher.h +33 -0
  166. package/src/native/Recordings/build.sh +1990 -29
  167. package/src/native/Recordings/RecordingsLib/MenuBarPopover.swift +0 -380
package/README.md CHANGED
@@ -11,6 +11,259 @@ Speech-to-text recording tool with MCP and CLI — records, transcribes, and opt
11
11
  npm install -g @hasna/recordings
12
12
  ```
13
13
 
14
+ ## macOS App
15
+
16
+ Recordings ships a **full native macOS app** (SwiftUI, macOS 26 / Liquid Glass) with a
17
+ companion menu-bar control. It opens to a **Recordings workspace**: a narrow violet
18
+ Liquid-Glass sidebar (Workspace · Library · Projects · Modes · Machines) beside one
19
+ continuous canvas with the record hero, transcript library, and detail view. The menu bar
20
+ provides recording controls and access to the main window while it is in the background.
21
+
22
+ - **Record** — large push-to-talk / dictation / command hero with live transcription,
23
+ duration, the active project, and a "just now" strip. Global shortcut (default F5, or
24
+ hold fn) works while the window is in the background.
25
+ - **Library** — every past transcript (read straight from the active local or HTTP Store
26
+ the CLI and MCP write), searchable and filterable by project, mode, and machine, with a
27
+ detail pane (copy, paste-into-front-app, audio playback, metadata).
28
+ - **Projects** — app projects are registered through the same canonical Store before a
29
+ recording can reference them, preserving referential integrity in local and remote modes.
30
+ - **Settings** (⌘,) — OpenAI key, language, recording shortcut, permissions, projects,
31
+ and voice shortcuts.
32
+
33
+ The app embeds a same-version `recordings` CLI as its data layer, so the CLI, MCP, and app
34
+ share one store without depending on a possibly stale global CLI installation. Production
35
+ release installs use a one-time managed bootstrap at `/Applications/Recordings.app`; later
36
+ release updates replace only that app through the installed root-owned broker.
37
+
38
+ ```bash
39
+ # One-time production bootstrap, run by MDM/root with independently authenticated values:
40
+ # The three input files must use canonical paths beneath root-owned, non-writable directories.
41
+ sudo packaging/macos/managed_bootstrap.sh \
42
+ --artifact /path/to/Recordings-0.2.13-macos-initial-bootstrap-updater.pkg \
43
+ --manifest /path/to/Recordings-0.2.13-macos-initial-bootstrap.manifest.json \
44
+ --envelope /path/to/Recordings-0.2.13-macos-initial-bootstrap-updater.bootstrap-envelope.json \
45
+ --expected-package-sha256 AUTHENTICATED_PACKAGE_SHA256 \
46
+ --expected-installer-team-id TEAMID1234 \
47
+ --expected-installer-certificate-sha256 AUTHENTICATED_INSTALLER_CERTIFICATE_SHA256
48
+
49
+ # After bootstrap, install only a signed app artifact through the immutable cohort:
50
+ recordings app install \
51
+ --artifact /path/to/Recordings-0.2.13-macos-app-update.zip \
52
+ --manifest /path/to/Recordings-0.2.13-macos-app-update.manifest.json \
53
+ --envelope /path/to/Recordings-0.2.13-macos-app-update.update-envelope.json \
54
+ --manifest-sha256 AUTHENTICATED_MANIFEST_SHA256 \
55
+ --expected-source-sha APPROVED_40_CHARACTER_COMMIT_SHA \
56
+ --expected-version 0.2.13 \
57
+ --expected-hostname station03 \
58
+ --expected-team-id TEAMID1234
59
+
60
+ recordings app open # launch it
61
+ recordings app status # show install state
62
+ /Applications/Recordings.app/Contents/Helpers/recordings-update-client status
63
+
64
+ # Release builds run only as the isolated _recordingsbuild account. Provision these first:
65
+ # - /private/var/recordings-build owned by _recordingsbuild, mode 0700, beneath a
66
+ # root-owned non-writable parent;
67
+ # - /Library/Application Support/Hasna/Recordings/BuildTrust/isolated-builder-v1
68
+ # as a root-owned non-linked mode-0444 file containing recordings-isolated-builder-v1;
69
+ # - an absolute Bun executable, clean source commit, locked dependencies, Swift/Xcode,
70
+ # universal native guard, unlocked Developer ID Application identity, notarytool profile,
71
+ # and 32-byte Ed25519 public/private release-envelope keys (private key owner-only).
72
+ cd src/native/Recordings
73
+ # Fresh source checkouts must install the locked JavaScript dependencies first:
74
+ (cd ../../.. && /absolute/path/to/bun install --frozen-lockfile)
75
+
76
+ # One-time initial bootstrap. This is the only subtype that accepts an Installer identity;
77
+ # it does not accept RECORDINGS_RELEASE_COMPATIBLE_COHORT_MANIFEST and emits no update envelope.
78
+ HOME="/private/var/recordings-build/home" \
79
+ BUN_EXECUTABLE="/absolute/path/to/bun" \
80
+ RECORDINGS_CODESIGN_IDENTITY="Developer ID Application: ..." \
81
+ RECORDINGS_INSTALLER_CODESIGN_IDENTITY="Developer ID Installer: ..." \
82
+ RECORDINGS_EXPECTED_TEAM_IDENTIFIER="TEAMID1234" \
83
+ RECORDINGS_NOTARY_KEYCHAIN_PROFILE="recordings-notary" \
84
+ RECORDINGS_RELEASE_SEQUENCE="1" \
85
+ RECORDINGS_RELEASE_KEY_EPOCH="1" \
86
+ RECORDINGS_RELEASE_ENVELOPE_EXPIRES_AT_UTC="2026-08-01T00:00:00.000Z" \
87
+ RECORDINGS_RELEASE_ENVELOPE_PRIVATE_KEY="/absolute/private/envelope-key.raw" \
88
+ RECORDINGS_RELEASE_ENVELOPE_PUBLIC_KEY="/absolute/public/envelope-key.raw" \
89
+ ./build.sh release initial-bootstrap
90
+
91
+ # After independent review, root-preauthorize the exact emitted cohort for later updates.
92
+ COHORT_MANIFEST="/private/var/recordings-build/release-output/Recordings-0.2.13-macos-initial-bootstrap-updater.compatible-cohort.json"
93
+ COHORT_DIGEST="$(/usr/bin/awk 'NR == 1 { print $1 }' "${COHORT_MANIFEST}.sha256")"
94
+ test "$(/usr/bin/shasum -a 256 "${COHORT_MANIFEST}" | /usr/bin/awk '{ print $1 }')" = "${COHORT_DIGEST}"
95
+ sudo /usr/bin/install -d -o root -g wheel -m 0755 \
96
+ "/Library/Application Support/Hasna/Recordings/BuildTrust/compatible-cohorts"
97
+ sudo /usr/bin/install -o root -g wheel -m 0444 "${COHORT_MANIFEST}" \
98
+ "/Library/Application Support/Hasna/Recordings/BuildTrust/compatible-cohorts/${COHORT_DIGEST}.json"
99
+
100
+ # App-only update. Copy the reviewed bootstrap cohort manifest into the root-owned
101
+ # compatible-cohorts directory under its exact SHA-256 filename before invoking this.
102
+ # No Installer identity, PKG, broker, verifier, bootstrap preflight, marker, or bootstrap
103
+ # envelope is used or emitted by this subtype.
104
+ HOME="/private/var/recordings-build/home" \
105
+ BUN_EXECUTABLE="/absolute/path/to/bun" \
106
+ RECORDINGS_CODESIGN_IDENTITY="Developer ID Application: ..." \
107
+ RECORDINGS_EXPECTED_TEAM_IDENTIFIER="TEAMID1234" \
108
+ RECORDINGS_NOTARY_KEYCHAIN_PROFILE="recordings-notary" \
109
+ RECORDINGS_RELEASE_SEQUENCE="2" \
110
+ RECORDINGS_RELEASE_KEY_EPOCH="1" \
111
+ RECORDINGS_RELEASE_ENVELOPE_EXPIRES_AT_UTC="2026-08-15T00:00:00.000Z" \
112
+ RECORDINGS_RELEASE_ENVELOPE_PRIVATE_KEY="/absolute/private/envelope-key.raw" \
113
+ RECORDINGS_RELEASE_ENVELOPE_PUBLIC_KEY="/absolute/public/envelope-key.raw" \
114
+ RECORDINGS_RELEASE_COMPATIBLE_COHORT_MANIFEST="/Library/Application Support/Hasna/Recordings/BuildTrust/compatible-cohorts/AUTHENTICATED_COHORT_SHA256.json" \
115
+ ./build.sh release app-update
116
+
117
+ # Explicit local-only alternative when Developer ID credentials are unavailable.
118
+ # Build on a Mac other than the approved target; this does not replace a release:
119
+ RECORDINGS_LOCAL_APPROVED_TARGET="station06" \
120
+ RECORDINGS_LOCAL_APPROVED_TARGET_IDENTITY_KIND="tailscale_node_id_sha256" \
121
+ RECORDINGS_LOCAL_APPROVED_TARGET_IDENTITY_SHA256="AUTHENTICATED_TAILSCALE_NODE_ID_SHA256" \
122
+ BUN_EXECUTABLE="/absolute/path/to/bun" \
123
+ ./build.sh local
124
+
125
+ # Install only on that exact target, with the immutable manifest digest recorded separately:
126
+ recordings app install \
127
+ --artifact /path/to/Recordings-0.2.13-macos-station06-local-only.zip \
128
+ --manifest /path/to/Recordings-0.2.13-macos-station06-local-only.manifest.json \
129
+ --manifest-sha256 AUTHENTICATED_MANIFEST_SHA256 \
130
+ --expected-source-sha APPROVED_40_CHARACTER_COMMIT_SHA \
131
+ --expected-version 0.2.13 \
132
+ --artifact-policy local-only \
133
+ --approved-target station06 \
134
+ --approved-target-identity-kind tailscale_node_id_sha256 \
135
+ --approved-target-identity-sha256 AUTHENTICATED_TAILSCALE_NODE_ID_SHA256 \
136
+ --acknowledge-local-signing-and-permissions \
137
+ --launch
138
+ swift test # run the native test suite
139
+ ```
140
+
141
+ The production release location is `/Applications/Recordings.app`. The managed bootstrap installs
142
+ one signed/notarized PKG exactly once, including the root broker, no-login verifier, launchd policy,
143
+ release key, and initial app. That root cohort is intentionally immutable:
144
+ `lifecycle=bootstrap-v1-app-updates-only`,
145
+ `root_maintenance_supported=false`, and `key_rotation_supported=false`. Subsequent release
146
+ envelopes must bind the exact installed broker/verifier cohort, protocol version, and pinned key
147
+ epoch, and may replace only `/Applications/Recordings.app`. A second bootstrap PKG, a broker or
148
+ verifier mismatch, a broker-protocol incompatibility, or a key-epoch change fails before app
149
+ activation with `unsupported_lifecycle`. Root updater maintenance and release-key rotation require a
150
+ separate managed reprovisioning lifecycle; the current tooling does not run Installer or overwrite
151
+ root trust as an update fallback.
152
+
153
+ Managed deployments should prefer a root-owned, `root:wheel` `/Applications` directory with mode
154
+ `0755`. The updater intentionally also accepts the macOS-compatible `root:admin` mode `0775`, but
155
+ that compatibility permits local admin-group actors to race application-namespace operations and
156
+ force fail-closed recovery or another availability loss. It does not authorize an update or bypass
157
+ the signed release, code-signing, or audit-token peer checks.
158
+
159
+ Before Installer runs, the managed bootstrap copies all three release inputs into a root-private
160
+ snapshot, verifies the out-of-band PKG digest, Gatekeeper decision, Installer certificate and Team
161
+ ID, expands that exact PKG without installing it, and runs its separately Developer-ID-signed
162
+ `recordings-bootstrap-preflight` executable. The preflight validates the Ed25519 signature and
163
+ expiry plus the package, manifest, app tree, code requirements, protected components, bootstrap
164
+ marker, key epoch, and installer-certificate bindings. Installer is never used when preflight
165
+ fails. A retry after a crash may skip Installer only when the complete immutable cohort is already
166
+ present and the absent, highest-seen, or committed release state exactly matches the same signed
167
+ bootstrap; partial or conflicting cohort evidence fails closed.
168
+
169
+ An `initial-bootstrap` release emits the stapled PKG, its digest and notary evidence, the
170
+ bootstrap envelope, and a schema-v2 compatible-cohort manifest plus its digest. The release
171
+ operator must retain that complete bootstrap set as the canonical onboarding artifact for the
172
+ cohort. New machines are onboarded only with that retained PKG set; a later `app-update` must
173
+ never be substituted for it, and rebuilding the same version is not a recovery mechanism.
174
+ Replacing any root component, Installer certificate, or release key requires a separately
175
+ approved managed-reprovisioning lifecycle.
176
+
177
+ Each `app-update` build requires
178
+ `RECORDINGS_RELEASE_COMPATIBLE_COHORT_MANIFEST` to name a root-owned, mode-0444,
179
+ content-addressed JSON file under
180
+ `/Library/Application Support/Hasna/Recordings/BuildTrust/compatible-cohorts/`. Its schema must
181
+ be exactly version `2` with no additional or missing keys. It authorizes exactly lifecycle
182
+ `bootstrap-v1-app-updates-only`, protocol version `1`, the current pinned key epoch,
183
+ `root_maintenance_supported=false`, and `key_rotation_supported=false`, together with the
184
+ finalized stapled cohort PKG, bootstrap marker, Installer certificate, exact release-envelope
185
+ public-key SHA-256, broker and verifier digests and designated requirements, Team ID, and minimum
186
+ broker version. Every source-directory ancestor must be canonical, root-owned, and non-writable;
187
+ the builder validates the content-addressed source once, copies it into its private build
188
+ directory, and reads only that stable snapshot. The builder refuses a mutable, misnamed, broader,
189
+ schema-mismatched, key-mismatched, or otherwise incompatible cohort manifest.
190
+
191
+ Release installation accepts only a finalized ZIP plus its manifest, signed update envelope, and
192
+ operator-supplied authenticated provenance. The manifest binds the bundle identifier, version,
193
+ source commit, architectures, pinned Team ID, designated-requirement digest, companion version and
194
+ hash, complete app-tree hash, archive hash, and trusted signing timestamps. The root broker verifies
195
+ the signed envelope, immutable cohort, monotonic release state, isolated verifier result, and
196
+ candidate code identity before transactional activation. Compatible app-only updates preserve the
197
+ stable signing identity and do not reset Microphone or Accessibility permissions. The updater never
198
+ calls `tccutil reset`, clears quarantine, re-signs an artifact, or builds on the target machine. A
199
+ private, fsynced root journal and anti-rollback state recover interrupted app replacement before the
200
+ next install attempt.
201
+
202
+ The explicit `local-only` development path remains separate and installs
203
+ `~/Applications/Recordings.app`; it never provisions or imitates the production root cohort.
204
+ For a station-specific deployment, pass `--expected-hostname` so the installer proves the live
205
+ short hostname before taking a lock or mutating state while the release artifact itself remains
206
+ fleet-distributable. Obtain `AUTHENTICATED_MANIFEST_SHA256` from independently authenticated
207
+ release evidence (for example, signed release metadata or the reviewed release ledger), never by
208
+ hashing the co-delivered manifest on the target. The release build prints the manifest digest for
209
+ that separate recording step. Release mode runs the native Swift test suite before compiling and
210
+ rechecks the clean pinned source commit before provenance and finalization.
211
+
212
+ An unattended build is valid only when the Developer ID private key and notarization profile are
213
+ already provisioned, unlocked, and authorized on the non-target builder. Tooling does not bypass a
214
+ Keychain approval prompt or manufacture credential authority. Likewise, SSH runtime smoke binds
215
+ evidence to the exact executable path but cannot prove foreground/key-window behavior; final UI
216
+ acceptance requires a logged-in console session or equivalent trusted GUI automation.
217
+
218
+ The `local` build mode is an explicit, target-scoped exception. It still requires a clean source
219
+ commit, an immutable ZIP and manifest, matching app/helper architectures and hashes, consistent
220
+ ad-hoc signatures, transactional state backup and rollback, and exact-path postactivation probes.
221
+ It is intentionally marked `local_only` and `non_notarized`, binds only SHA-256 digests of the
222
+ approved target and non-target builder identities, never runs notarization or Gatekeeper release
223
+ checks, and cannot be installed without matching the live Mac name and acknowledging that the
224
+ changed signing identity can require manual Microphone or Accessibility reauthorization. For new
225
+ artifacts, use `tailscale_node_id_sha256`: the authenticated operator input is the SHA-256 of the
226
+ exact target's Tailscale node ID. The non-target builder separately reads and hashes its own live
227
+ online `Self.ID`, records `builder_identity_kind=tailscale_node_id_sha256`, and requires that
228
+ same-namespace digest to differ from the target digest. Before locking or mutating local state, the
229
+ installer ignores caller `PATH` and accepts only the canonical standard app at
230
+ `/Applications/Tailscale.app`. Using pinned macOS system tools and a clean environment, it
231
+ cryptographically verifies the complete app and its CLI against Tailscale's official
232
+ `TeamIdentifier` (`W5364U7YZB`) and bundle identifier (`io.tailscale.ipn.macsys`). It then copies
233
+ the complete app into a mode-700 installer-owned temporary directory, re-verifies the copied app
234
+ and CLI, and reads `tailscale status --json` only from that private snapshot after one final
235
+ signature check. The mutable `/Applications` path is never executed, caller status environment is
236
+ not inherited, and all snapshot paths are removed by normal installer cleanup. The builder applies
237
+ the same checks in its private build directory and removes the snapshot with the rest of the build
238
+ workspace. The status parser then
239
+ requires online `Self` with hostname `station06`,
240
+ requires the single nonempty `Self.ID` to contain no whitespace or NUL, hashes its exact decoded
241
+ bytes without a newline, and compares the digest. Neither raw node ID is written to the manifest,
242
+ build log, or installer log. Older schema-v3 artifacts without an
243
+ identity-kind field remain compatible as `hardware_uuid_sha256`; that kind is retained only for
244
+ backward compatibility. The installer never resets or inspects TCC and never clears quarantine in
245
+ either policy.
246
+
247
+ Requires macOS 26+; source builds also require a Swift toolchain (Xcode or Command Line Tools).
248
+ Release packaging also requires the universal descriptor-guard prebuild at
249
+ `scripts/native/prebuilds/darwin-universal/recordings_fs_guard.node`. Build it on a trusted Mac
250
+ with `bun run build:native-fs-guard`; target installers never compile native recovery code and
251
+ fail before creating install state when the pinned prebuild is missing or unsafe.
252
+ Set the OpenAI API key in **Settings** or via `recordings` config;
253
+ transcription/enhancement use it.
254
+
255
+ The app's **Transcription Cleanup** setting controls the same post-processing pipeline as
256
+ the CLI and MCP server. Use **Raw** to keep verbatim text only, **Auto** to clean up only
257
+ when trigger phrases or instruction patterns are detected, or **Always** to run the
258
+ transcriber cleanup prompt for every recording. Global cleanup instructions can be set in
259
+ Settings, and project-specific instructions are appended when a project is active.
260
+
261
+ The native app uses OpenAI realtime transcription for the stop-and-paste path: settled
262
+ `gpt-realtime-whisper` text is saved and pasted immediately, while full-file
263
+ `gpt-4o-transcribe` remains the bounded quality fallback when realtime is empty,
264
+ unsettled, or cannot be saved. Raw and processed transcript fields are still stored
265
+ separately, so cleanup instructions never replace the verbatim transcript.
266
+
14
267
  ## CLI Usage
15
268
 
16
269
  ```bash
@@ -20,13 +273,94 @@ recordings --help
20
273
  - `recordings record`
21
274
  - `recordings transcribe <file>`
22
275
  - `recordings transcribe <file> --stream`
276
+ - `recordings transcribe <file> --prompt "DALL-E, Hasna, gpt-4o"`
277
+ - `recordings transcribe <file> --transcriber-prompt "Clean up punctuation only" --post-processing always`
278
+ - `recordings save-text --text-file transcript.txt --source realtime_fast_path`
23
279
  - `recordings rewrite <text> --instruction "<instruction>"`
24
- - `recordings list`
25
- - `recordings show <id>`
26
- - `recordings search <query>`
280
+ - `recordings list --limit 20 --cursor 0`
281
+ - `recordings list --verbose`
282
+ - `recordings show <id>` / `recordings inspect <id>`
283
+ - `recordings search <query> --limit 20 --cursor 0`
27
284
  - `recordings delete <id>`
28
285
  - `recordings stats`
29
286
 
287
+ ### Compact Output
288
+
289
+ Agent-facing list commands are compact by default. Terminal output shows bounded
290
+ rows, short text previews, totals, pagination cursors, and the next detail command
291
+ instead of dumping full recording objects.
292
+
293
+ ```bash
294
+ recordings list # compact rows, default limit 20
295
+ recordings list --cursor 20 # next page
296
+ recordings list --verbose # more metadata, still no full transcript dump
297
+ recordings show <id> # full recording detail
298
+ recordings inspect <id> # alias for show
299
+ recordings --json list -n 100 # machine-readable records for integrations
300
+ ```
301
+
302
+ Terminal list output is capped at 50 rows. JSON list output preserves complete
303
+ recording objects and accepts up to 500 rows per page.
304
+
305
+ ### Transcription Prompts
306
+
307
+ Recordings separates speech-to-text context from post-transcription cleanup:
308
+
309
+ - `--prompt` / `transcription_prompt` is passed to the OpenAI audio transcription
310
+ request as vocabulary or context. Use it for names, acronyms, technical terms, or
311
+ preceding segment context.
312
+ - `--transcriber-prompt` / `transcriber_prompt` is used after raw transcription by the
313
+ text transcriber pipeline. Use it for cleanup, formatting, tone, summaries, or
314
+ transformations.
315
+ - `--post-processing off|auto|always` controls whether cleanup runs. `--no-enhance` is a
316
+ compatibility alias for `off`.
317
+
318
+ Examples:
319
+
320
+ ```bash
321
+ # Verbatim dictation, no cleanup
322
+ recordings transcribe meeting.wav --post-processing off
323
+
324
+ # Better STT recognition for names and acronyms, still verbatim
325
+ recordings transcribe demo.wav --prompt "Hasna, Alumia, DALL-E, gpt-4o"
326
+
327
+ # Always clean up punctuation and paragraphs after raw transcription
328
+ recordings transcribe note.wav \
329
+ --post-processing always \
330
+ --transcriber-prompt "Fix punctuation and paragraph breaks. Preserve the speaker's meaning."
331
+
332
+ # Auto mode only cleans up when the transcript asks for it, such as "say it better"
333
+ recordings transcribe draft.wav --post-processing auto
334
+ ```
335
+
336
+ Persistent config can be stored in `~/.hasna/recordings/config.json` or a project-local
337
+ `.recordings/config.json`:
338
+
339
+ ```json
340
+ {
341
+ "transcription_prompt": "Hasna, Alumia, gpt-4o",
342
+ "transcriber_prompt": "Clean up grammar and format as concise Markdown notes.",
343
+ "post_processing_mode": "always",
344
+ "enhancement_model": "gpt-4o"
345
+ }
346
+ ```
347
+
348
+ Environment overrides are also supported:
349
+
350
+ ```bash
351
+ export RECORDINGS_TRANSCRIPTION_PROMPT="Hasna, DALL-E, gpt-4o"
352
+ export RECORDINGS_TRANSCRIBER_PROMPT="Format as polished meeting notes"
353
+ export RECORDINGS_POST_PROCESSING_MODE=always
354
+ export RECORDINGS_TRANSCRIBER_MODEL=gpt-4o
355
+ export RECORDINGS_MODEL=gpt-4o-transcribe
356
+ export RECORDINGS_REALTIME_SESSION_MODEL=gpt-realtime
357
+ export RECORDINGS_REALTIME_TRANSCRIPTION_MODEL=gpt-realtime-whisper
358
+ ```
359
+
360
+ `RECORDINGS_MODEL` is the bounded file-transcription model. Realtime session and realtime
361
+ transcription models are separate slots; `recordings check --json` reports all three and
362
+ includes `config_warnings` if a model is placed in the wrong slot.
363
+
30
364
  ## MCP Server
31
365
 
32
366
  ```bash
@@ -36,8 +370,8 @@ recordings-mcp
36
370
  ## HTTP mode
37
371
 
38
372
  ```bash
39
- recordings-mcp --http # default port 8829
40
- MCP_HTTP=1 MCP_HTTP_PORT=8829 recordings-mcp
373
+ recordings-mcp --http # default port 8873
374
+ MCP_HTTP=1 MCP_HTTP_PORT=8873 recordings-mcp
41
375
  ```
42
376
 
43
377
  Endpoints: `GET /health` → `{"status":"ok","name":"recordings"}`, MCP at `/mcp`.
@@ -85,38 +419,19 @@ const client = new RecordingsV1Client({
85
419
  const { recordings } = await client.listRecordings({ limit: 20 });
86
420
  ```
87
421
 
88
- ## Storage Sync
89
-
90
- This package has native local/remote sync. Local data stays in SQLite under
91
- `~/.hasna/recordings/`; remote sync uses PostgreSQL when
92
- `HASNA_RECORDINGS_DATABASE_URL` is set or `~/.hasna/recordings/storage/config.json` is
93
- configured.
94
-
95
- The optional config file uses a `postgres` object:
96
-
97
- ```json
98
- {
99
- "mode": "remote",
100
- "postgres": {
101
- "host": "db.example",
102
- "port": 5432,
103
- "username": "recordings",
104
- "password_env": "RECORDINGS_DATABASE_PASSWORD",
105
- "ssl": true
106
- }
107
- }
108
- ```
422
+ Useful agent tools include `recordings_status` for safe service/config diagnostics,
423
+ `transcribe_audio`, `save_recording`, `list_recordings`, `search_recordings`,
424
+ `register_agent`, `heartbeat`, and `set_focus`.
109
425
 
110
- ```bash
111
- recordings storage status
112
- recordings storage migrate
113
- recordings storage push
114
- recordings storage pull
115
- ```
426
+ MCP `list_recordings` and `search_recordings` are compact by default. Compact output
427
+ is capped at 50 rows, `full=true` metadata rows are capped at 10, previews remain
428
+ bounded, and results include next-cursor hints. Use `get_recording { id }` for full
429
+ transcript details.
116
430
 
117
- `RECORDINGS_DATABASE_URL` is accepted as the non-Hasna fallback database URL.
118
- `HASNA_RECORDINGS_STORAGE_CONFIG` can point automation at a non-default storage
119
- config file.
431
+ For MCP, `transcribe_audio` accepts `transcription_prompt` (or legacy `prompt`) for STT
432
+ context, `transcriber_prompt` for cleanup instructions, and `post_processing_mode` with
433
+ `off`, `auto`, or `always`. Tool results preserve `raw_text` and return `processed_text`
434
+ only when post-processing actually produced enhanced output.
120
435
 
121
436
  ## Data Directory
122
437
 
package/bun.lock CHANGED
@@ -17,6 +17,7 @@
17
17
  "devDependencies": {
18
18
  "@types/bun": "^1.2.5",
19
19
  "@types/pg": "^8.11.11",
20
+ "node-api-headers": "1.9.0",
20
21
  "typescript": "^5.8.2",
21
22
  },
22
23
  },
@@ -154,6 +155,8 @@
154
155
 
155
156
  "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
156
157
 
158
+ "node-api-headers": ["node-api-headers@1.9.0", "", {}, "sha512-2oNILP4jXwRB4ywnYKjVk1YyJ96n2D4EOVJO6S3oYZ5PtbJrw3Yt9TpAuX3nBLMuzn74rnfGQrv13pS9vC+YiA=="],
159
+
157
160
  "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
158
161
 
159
162
  "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
@@ -0,0 +1,2 @@
1
+ export declare function ensureNativeFsGuardAddon(repositoryRoot?: string): string;
2
+ //# sourceMappingURL=native-fs-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-fs-guard.d.ts","sourceRoot":"","sources":["../../../src/__tests__/helpers/native-fs-guard.ts"],"names":[],"mappings":"AAKA,wBAAgB,wBAAwB,CAAC,cAAc,SAAuC,GAAG,MAAM,CAsCtG"}