Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 14 additions & 6 deletions INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@ These steps are written for someone who does not normally install preview softwa

## Before you start

You need an Apple silicon Mac, macOS 14 or later, about 1 GB of free disk space, and an internet connection. RecordCove works locally and does not require a RecordCove account.
You need an Apple silicon Mac, macOS 14 or later, at least 6 GB of free disk space during installation, and an internet connection for the initial download. RecordCove works locally after installation and does not require a RecordCove account.

The preview is a large download because it includes two verified local models: **Whisper Small English** for transcription and **IBM Granite 4.1 3B** for titles, summaries, and transcript enhancement. Both are installed with RecordCove. Granite is selected by default so a new tester can evaluate the intended local Understanding experience without a second model download. Apple on-device and Ministral remain optional choices in Models.

This early preview is ad-hoc signed and is not Apple-notarized. Install it only if you trust Preston Core Systems Limited, downloaded it from `https://preview.recordcove.com`, and the filename and checksum match the preview page.

## Download and unzip

1. Open `https://preview.recordcove.com/downloads.html` in Safari, Chrome, or another browser.
2. Choose the newest accepted preview. The page shows its publication date and remembers the version selected in that browser so it can highlight a later update.
3. Wait for the download to finish. It is large because Whisper Small English is included.
3. Wait for the download to finish. The current bundled-model candidate is about 2.53 GB as a ZIP and about 2.5 GB after extraction. Keeping at least 6 GB free allows the ZIP and extracted application to exist together during installation.
4. Open Finder and select **Downloads** in the left sidebar.
5. Double-click `RecordCove-macOS-preview.zip`. Your Mac creates a folder containing `RecordCove.app`.
6. Drag **RecordCove** into the **Applications** folder in Finder.
Expand All @@ -24,11 +26,17 @@ The Downloads page shows the exact SHA-256 checksum for every preview. If you ar
## First launch

1. Open **Applications** in Finder.
2. Hold the Control key while clicking **RecordCove**, then select **Open**.
3. macOS explains that Apple cannot verify the developer. Select **Open** only if the file and checksum match the preview page.
4. RecordCove should open. If macOS says the app is damaged, stop and tell the maintainer.
2. Double-click **RecordCove** once. macOS may explain that Apple cannot verify the app is free of malware.
3. If the alert offers only **Move to Bin** and **Done**, select **Done**. Select **Move to Bin** instead if the source, filename, or checksum does not match the preview page.
4. Open the Apple menu, select **System Settings**, then select **Privacy & Security**.
5. Scroll down to **Security**. Confirm that the message names **RecordCove**, then select **Open Anyway**.
6. Authenticate with your Mac login password or Touch ID if macOS asks.
7. The warning appears again. After confirming the app name, source, filename, and checksum, select **Open**.
8. RecordCove should open. macOS saves this as an exception for this specific copy of the app.

The **Open Anyway** button is available for about one hour after a blocked launch. If it is missing, try to open RecordCove once more and immediately return to **Privacy & Security**. If it still does not appear, or macOS says the app is damaged, stop and tell the maintainer.

Do not disable Gatekeeper and do not run a Terminal command to remove quarantine.
Do not disable Gatekeeper, lower **Allow applications from**, or use Terminal workarounds such as `xattr`, `spctl`, `sudo`, or removal of `com.apple.quarantine`.

## Microphone permission

Expand Down
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

This public, preview-only repository controls the RecordCove pre-release portal, release manifest, public prerelease archive, and non-technical tester guidance. Product source code, recordings, transcripts, model files, tester data, and credentials are not stored here.

The tester portal is `https://preview.recordcove.com`. The exact archive is attached to the manifest-pinned public GitHub prerelease after its revision, byte size, SHA-256 checksum, bundle identity, and ad-hoc signature are verified. Testers do not need a GitHub account to download a public release asset.
The tester portal is `https://preview.recordcove.com`. The exact archive is linked by the manifest-pinned public GitHub prerelease after its revision, byte size, SHA-256 checksum, bundle identity, and ad-hoc signature are verified. Current large bundled-model archives use the immutable `downloads.preview.recordcove.com` object path; earlier accepted previews may remain GitHub-hosted. Testers do not need a GitHub account.

The Downloads page reads the public GitHub prerelease inventory and displays only releases at or below the manifest-pinned accepted preview. The newest displayed asset must match the manifest URL, byte size, and SHA-256 digest exactly. A newer staged GitHub prerelease is therefore not promoted before its manifest is accepted. The page remembers the last selected version only in that browser's local storage; no download history is uploaded.
The Downloads page reads the public GitHub prerelease inventory and displays only releases at or below the manifest-pinned accepted preview. The newest metadata-only release must name the same immutable download URL that the manifest binds to the exact byte size and SHA-256 digest. Historical GitHub assets retain their provider digest checks. A newer staged prerelease is therefore not promoted before its manifest is accepted. The page remembers the last selected version only in that browser's local storage; no download history is uploaded.

The preview is not Apple-notarized and is not the public product launch. The portal must retain that warning and the standard Finder control-click **Open** instructions. It must never tell testers to disable Gatekeeper or run a quarantine-removal command.
The preview is not Apple-notarized and is not the public product launch. The portal must retain that warning and the current Gatekeeper recovery path: attempt one launch, choose **Done** if macOS offers only **Move to Bin** and **Done**, then use **System Settings**, **Privacy & Security**, **Open Anyway**, authentication, and the final **Open** confirmation. It must never tell testers to disable Gatekeeper, lower app-security settings, or use Terminal commands to remove quarantine.

The next bundled-model preview includes verified Whisper Small English and IBM Granite 4.1 3B inside the application. Whisper is the default transcription model and Granite is the default Understanding model for titles, summaries, and enhancement. Testers must not need a second Granite download or be silently moved to Apple on-device. The bundled-model archive exceeds GitHub Release's single-asset limit. Its exact accepted byte count and SHA-256 checksum must come only from the immutable release manifest generated from the final verified package, never from a hand-maintained README value. The protected publisher uses multipart staging in `recordcove-preview-downloads`, verifies the staged bytes, conditionally promotes an unused immutable key, verifies the final object, and then hashes the complete public response from `downloads.preview.recordcove.com` before staging the manifest. Provider credentials never enter the M3 build runner.

## Current model scope

Expand All @@ -20,11 +22,12 @@ This is a current release-scope and supply-chain qualification decision. It is n
2. Match the portal manifest to the archive filename, byte size, and SHA-256 checksum.
3. Keep the product repository and all user content out of this preview-only repository.
4. Deploy the static portal only to the Terraform-managed Pages project.
5. Upload the exact archive only to the manifest-pinned public prerelease.
5. Upload the exact archive only through the protected immutable R2 publisher and keep the GitHub prerelease metadata-only for the bundled-model lane.
6. Verify the portal, download, checksum, extraction, and first-launch guide from an independent browser session.
7. Keep Developer ID signing, notarization, Homebrew, trusted CI, and public product launch outside this preview lane.
8. Use only privacy-safe product imagery with synthetic content. Public screenshots must never show a real recording, transcript, summary, prompt, model output, filename, path, category, tag, or other user data.
9. Version static CSS and image URLs when their bytes change so cached preview pages cannot combine new markup with stale presentation rules.
10. Explain that both default local models are included, why the archive is large, and that at least 6 GB of temporary free space is recommended for download plus extraction.

Tester instructions are in [INSTALL.md](INSTALL.md) and [TESTING.md](TESTING.md). The distribution boundary is in [PRIVATE_PREVIEW_NOTICE.md](PRIVATE_PREVIEW_NOTICE.md).

Expand Down
19 changes: 10 additions & 9 deletions TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,20 @@ The preview currently focuses model discovery and qualification on developers an

1. Open RecordCove.
2. Complete the four onboarding screens.
3. Confirm **Whisper Small English** is shown as included and verified.
4. Select **New Recording**.
5. Record about 20 seconds of ordinary, non-private speech.
6. Select **Stop and Save**.
7. Confirm the recording appears in **All Recordings** and does not say it was recovered after a normal save.
8. Play the recording.
9. Transcribe it with Whisper Small English.
10. Confirm the transcript covers the full recording.
3. Confirm **Whisper Small English** and **IBM Granite 4.1 3B** are both shown as included and verified. No second model download should be required.
4. Confirm Whisper Small English is selected for transcription and IBM Granite is selected for Understanding.
5. Select **New Recording**.
6. Record about 20 seconds of ordinary, non-private speech.
7. Select **Stop and Save**.
8. Confirm the recording appears in **All Recordings** and does not say it was recovered after a normal save.
9. Play the recording and transcribe it with Whisper Small English.
10. Generate a title and summary, then try **Enhance**. Confirm the local results complete without switching to Apple on-device unless you explicitly selected Apple yourself.
11. Confirm the transcript covers the full recording.

## Everyday feature checks

- Copy the transcript and confirm the button changes back from **Copied** to **Copy** after a short moment.
- Generate a Summary with an installed Understanding model.
- Regenerate a Summary with IBM Granite selected.
- Try **Enhance** and confirm the original transcript remains available in Version History.
- Try **Clean up** and confirm the original transcript remains available.
- Create a Quick Note, type a title, press Return, and confirm the cursor moves into the note body.
Expand Down
16 changes: 11 additions & 5 deletions scripts/update-release.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ import { fileURLToPath } from "node:url";

const RELEASE_FILE = "RecordCove-macOS-preview.zip";
const RELEASE_REPOSITORY = "PrestonCoreSystems/recordcove-preview";
const TAG_PATTERN = /^v(\d+\.\d+\.\d+)-preview\.(\d+)$/;
const RELEASE_ORIGIN = "https://downloads.preview.recordcove.com";
const TAG_PATTERN = /^v(\d+\.\d+\.\d+)-preview\.([1-9]\d*)$/;
const SHA_PATTERN = /^[0-9a-f]{40}$/;
const DIGEST_PATTERN = /^[0-9a-f]{64}$/;
const MAXIMUM_ARCHIVE_BYTES = 4 * 1024 * 1024 * 1024;

function requireValue(value, label, pattern) {
if (!pattern.test(value)) {
Expand Down Expand Up @@ -42,17 +44,21 @@ async function openRegularFileNoFollow(filePath, expectedParent) {

export function validateReleaseManifest(manifest) {
const tagMatch = TAG_PATTERN.exec(manifest.releaseTag ?? "");
const currentDownloadUrl = `https://github.com/${RELEASE_REPOSITORY}/releases/download/${manifest.releaseTag}/${RELEASE_FILE}`;
const acceptedDownloadUrls = new Set([
`https://github.com/${RELEASE_REPOSITORY}/releases/download/${manifest.releaseTag}/${RELEASE_FILE}`,
`${RELEASE_ORIGIN}/previews/${manifest.releaseTag}/${RELEASE_FILE}`,
]);
if (
!tagMatch ||
manifest.product !== "RecordCove" ||
manifest.version !== tagMatch[1] ||
manifest.file !== RELEASE_FILE ||
manifest.downloadUrl !== currentDownloadUrl ||
!acceptedDownloadUrls.has(manifest.downloadUrl) ||
!SHA_PATTERN.test(manifest.sourceRevision ?? "") ||
!DIGEST_PATTERN.test(manifest.sha256 ?? "") ||
!Number.isSafeInteger(manifest.bytes) ||
manifest.bytes <= 0 ||
manifest.bytes > MAXIMUM_ARCHIVE_BYTES ||
manifest.signing !== "ad-hoc" ||
manifest.notarized !== false ||
manifest.publicReleaseApproved !== false ||
Expand All @@ -77,7 +83,7 @@ export async function updateRelease({
requireValue(archiveSha256, "archive SHA-256", DIGEST_PATTERN);

const bytes = Number(archiveBytes);
if (!Number.isSafeInteger(bytes) || bytes <= 0) {
if (!Number.isSafeInteger(bytes) || bytes <= 0 || bytes > MAXIMUM_ARCHIVE_BYTES) {
throw new Error("archive byte size is invalid");
}

Expand Down Expand Up @@ -108,7 +114,7 @@ export async function updateRelease({
) {
throw new Error("release tag must be the next preview for the current version");
}
const downloadUrl = `https://github.com/${RELEASE_REPOSITORY}/releases/download/${releaseTag}/${RELEASE_FILE}`;
const downloadUrl = `${RELEASE_ORIGIN}/previews/${releaseTag}/${RELEASE_FILE}`;
manifest.version = nextTagMatch[1];
manifest.releaseTag = releaseTag;
manifest.bytes = bytes;
Expand Down
2 changes: 1 addition & 1 deletion site/downloads.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ <h2 id="history-title">Previous previews</h2>
<section class="panel" aria-labelledby="remember-title">
<h2 id="remember-title">How version tracking works</h2>
<p>The marker is stored only in this browser when you select a download. It is not an account, it is not uploaded, and it cannot confirm that the download or installation finished.</p>
<p>GitHub currently serves the verified archive as <code>RecordCove-macOS-preview.zip</code>. The Downloads page records the exact preview version beside it; the installed app remains named <strong>RecordCove</strong>.</p>
<p>New bundled-model archives are served from the immutable RecordCove Preview download domain, while GitHub keeps the dated release notes. Earlier verified GitHub-hosted previews remain available in the same history. The installed app remains named <strong>RecordCove</strong>.</p>
</section>
</main>
<footer>Copyright <span id="year"></span> Preston Core Systems Limited. RecordCove preview.</footer>
Expand Down
39 changes: 35 additions & 4 deletions site/downloads.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const RELEASE_API = "https://api.github.com/repos/PrestonCoreSystems/recordcove-preview/releases";
const RELEASE_REPOSITORY = "PrestonCoreSystems/recordcove-preview";
const RELEASE_ORIGIN = "https://downloads.preview.recordcove.com";
const RELEASE_FILE = "RecordCove-macOS-preview.zip";
const RELEASE_PAGE_SIZE = 100;
const MAX_RELEASE_PAGES = 20;
Expand All @@ -26,25 +27,52 @@ export function compareReleaseTags(left, right) {
return 0;
}

export function normalizeRelease(release) {
export function normalizeRelease(release, manifest = null) {
const parts = releaseParts(release?.tag_name);
if (!parts || release.draft !== false || release.prerelease !== true) {
return null;
}
const expectedReleaseUrl = `https://github.com/${RELEASE_REPOSITORY}/releases/tag/${release.tag_name}`;
const expectedDownloadUrl = `https://github.com/${RELEASE_REPOSITORY}/releases/download/${release.tag_name}/${RELEASE_FILE}`;
const expectedR2DownloadUrl = `${RELEASE_ORIGIN}/previews/${release.tag_name}/${RELEASE_FILE}`;
const asset = release.assets?.find((candidate) => candidate.name === RELEASE_FILE);
const digestMatch = DIGEST.exec(asset?.digest ?? "");
const publishedAt = new Date(release.published_at ?? "");
if (
release.html_url !== expectedReleaseUrl ||
Number.isNaN(publishedAt.getTime())
) {
return null;
}
if (
manifest?.releaseTag === release.tag_name &&
manifest.downloadUrl === expectedR2DownloadUrl
) {
if (
!Array.isArray(release.assets) ||
release.assets.length !== 0 ||
!String(release.body ?? "").includes(`Verified download: ${expectedR2DownloadUrl}`)
) {
return null;
}
return {
tag: release.tag_name,
version: `${parts[0]}.${parts[1]}.${parts[2]}`,
previewNumber: parts[3],
publishedAt: publishedAt.toISOString(),
releaseUrl: release.html_url,
downloadUrl: manifest.downloadUrl,
bytes: manifest.bytes,
sha256: manifest.sha256,
};
}
if (
!asset ||
asset.state !== "uploaded" ||
asset.browser_download_url !== expectedDownloadUrl ||
!Number.isSafeInteger(asset.size) ||
asset.size <= 0 ||
!digestMatch ||
Number.isNaN(publishedAt.getTime())
!digestMatch
) {
return null;
}
Expand All @@ -62,7 +90,7 @@ export function normalizeRelease(release) {

export function acceptedReleases(rawReleases, manifest) {
const releases = rawReleases
.map(normalizeRelease)
.map((release) => normalizeRelease(release, manifest))
.filter(Boolean)
.filter((release) => compareReleaseTags(release.tag, manifest.releaseTag) <= 0)
.sort((left, right) => compareReleaseTags(right.tag, left.tag));
Expand Down Expand Up @@ -165,6 +193,9 @@ function formatDate(value) {
}

function formatBytes(bytes) {
if (bytes >= 1_000_000_000) {
return `${(bytes / 1_000_000_000).toFixed(2)} GB`;
}
return `${(bytes / 1_000_000).toFixed(1)} MB`;
}

Expand Down
6 changes: 5 additions & 1 deletion site/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,11 @@ <h2 id="privacy-title">Protect your private content</h2>
<p>Feedwish accepts feedback without an account and gives you a private management code for status checks or permanent withdrawal. Signing in is optional and is only needed to follow feedback.</p>
</section>
<section class="panel" aria-labelledby="models-title">
<h2 id="models-title">Current model scope</h2>
<h2 id="models-title">Two local models are included</h2>
<p><strong>Whisper Small English</strong> is included and selected for transcription. <strong>IBM Granite 4.1 3B</strong> is included and selected for titles, summaries, and transcript enhancement.</p>
<p>Including both verified models makes the download much larger, but it lets testers use RecordCove's intended local defaults without downloading another model after installation. Apple on-device and Ministral remain optional choices.</p>
<p>The bundled-model owner-preview candidate measures about 2.53 GB as a ZIP. Its download will remain unavailable until the exact large artifact has an approved and verified distribution path.</p>
<h3>Current qualification scope</h3>
<p>For this preview, RecordCove focuses model discovery and qualification on developers and originating model families from North America, Europe, and Africa.</p>
<p>Models originating from mainland China are outside the current evaluation and packaging scope. This is a release-scope decision, not a claim that included models are universally better or that developer region proves local privacy.</p>
</section>
Expand Down
Loading