diff --git a/docs/infrastructure/hyperyap-com.md b/docs/infrastructure/hyperyap-com.md new file mode 100644 index 0000000..dc489fa --- /dev/null +++ b/docs/infrastructure/hyperyap-com.md @@ -0,0 +1,145 @@ +# HyperYap.com Infrastructure + +Last verified: 2026-06-19 + +## Purpose + +`hyperyap.com` is the public website for HyperYap. The website should stay +separate from the Tauri desktop app surface and must preserve the project +privacy boundary: + +- no analytics or telemetry by default +- no hosted transcription endpoint +- no open CORS examples +- no copy that implies uploaded audio processing + +## Current Setup + +- Registrar: Namecheap +- DNS authority target: Cloudflare +- Hosting target: Railway +- Website source path: `website/` +- Website branch: `codex/hyperyap-com` +- GitHub repository: `avalonreset/hyperyap` +- Railway project: `hyperyap-com` +- Railway project ID: `b377377b-002c-4537-af5f-f3f3024b4110` +- Railway environment: `production` +- Railway environment ID: `02bcbd37-47b5-4495-9e43-9fcd2bc26f05` +- Railway service: `hyperyap-site` +- Railway service ID: `585ad33d-03b6-49bc-993e-e51d89d3e31e` +- Railway service domain: `https://hyperyap-site-production.up.railway.app/` +- Railway deployment trigger: `avalonreset/hyperyap`, branch `codex/hyperyap-com` +- Cloudflare zone: `hyperyap.com` +- Cloudflare zone ID: `e6c37dee7e277e6be62833f3c5cd2480` +- Cloudflare nameservers assigned: `hazel.ns.cloudflare.com`, `kaiser.ns.cloudflare.com` +- Current Cloudflare zone status: `pending` until Namecheap delegates to Cloudflare + +## Current Blocker + +Namecheap API calls are blocked by the account's API client-IP whitelist. The +DNS records already exist in Cloudflare, but the registrar still uses Namecheap +default nameservers: + +- `dns1.registrar-servers.com` +- `dns2.registrar-servers.com` + +To finish delegation, update the Namecheap API whitelist for the current +outgoing IP and rerun `namecheap.domains.dns.setCustom`, or change the +nameservers manually in the Namecheap dashboard to: + +- `hazel.ns.cloudflare.com` +- `kaiser.ns.cloudflare.com` + +After that propagates, Cloudflare should activate the zone and Railway should +verify the custom-domain TXT records. + +## Railway Domains + +Railway custom domains are configured for: + +| Domain | Traffic record | Verification host | +| --- | --- | --- | +| `hyperyap.com` | CNAME `hyperyap.com` -> `d9qs201m.up.railway.app` | TXT `_railway-verify.hyperyap.com` | +| `www.hyperyap.com` | CNAME `www.hyperyap.com` -> `qvv1bgf7.up.railway.app` | TXT `_railway-verify.www.hyperyap.com` | + +Cloudflare records were created as DNS-only so Railway can validate ownership +and serve HTTPS directly. + +## Provider API Notes + +### Cloudflare + +Use the Cloudflare v4 API with the bearer token already stored in the local +credential inventory. Do not print token values. + +Common calls: + +```text +GET /client/v4/user/tokens/verify +GET /client/v4/zones?name=hyperyap.com +POST /client/v4/zones +POST /client/v4/zones/{zone_id}/dns_records +``` + +For Railway domains, add the DNS records returned by Railway: + +- apex domain usually needs Railway's CNAME flattening target +- `www` can point to the Railway target or redirect at Cloudflare +- Railway also returns a TXT verification record that must be present + +### Namecheap + +Use Namecheap's XML API from a whitelisted client IP. Required fields are: + +- `ApiUser` +- `ApiKey` +- `UserName` +- `ClientIp` +- `Command` +- `SLD` +- `TLD` + +To delegate the domain to Cloudflare: + +```text +namecheap.domains.dns.setCustom +SLD=hyperyap +TLD=com +NameServers=hazel.ns.cloudflare.com,kaiser.ns.cloudflare.com +``` + +Do not switch Namecheap nameservers until Cloudflare has enough DNS records to +serve either the Railway site or an intentional temporary placeholder. + +### Railway + +The local Railway CLI session can expire. The Railway GraphQL API token in the +local credential inventory is the reliable path for API reads and mutations. + +Useful GraphQL mutations: + +- `projectCreate` +- `serviceCreate` +- `deploymentTriggerCreate` +- `serviceInstanceDeploy` +- `serviceDomainCreate` +- `customDomainCreate` +- `environmentPatchCommit` + +The site is a static service with a deployment trigger root directory of +`website/`. + +## Verification Checklist + +Before declaring the domain live: + +1. Railway deployment status is `SUCCESS`. +2. Railway-provided service domain returns the HyperYap homepage. +3. `hyperyap.com` and `www.hyperyap.com` records exist in Cloudflare. +4. Railway TXT verification record exists in Cloudflare. +5. Namecheap nameservers are set to Cloudflare. +6. `https://hyperyap.com/` returns `200`. +7. `http://hyperyap.com/` redirects to HTTPS. +8. `https://www.hyperyap.com/` redirects or resolves intentionally. +9. The live page contains no analytics, trackers, remote transcription endpoint, + or unexpected third-party scripts. diff --git a/website/api.html b/website/api.html new file mode 100644 index 0000000..306b41e --- /dev/null +++ b/website/api.html @@ -0,0 +1,76 @@ + + + + + + HyperYap Local API + + + + + + + + +
+
+

Experimental integration surface

+

Local HTTP API

+

+ HyperYap can expose a localhost-only transcription endpoint while the + desktop app is open. It is intended for local tools that need a private + speech-to-text engine. +

+ +
+

Enable it

+
    +
  1. Open HyperYap.
  2. +
  3. Go to Settings, then System.
  4. +
  5. Enable Local API.
  6. +
  7. Use the configured localhost port. The default is 4800.
  8. +
+
+ +
+

Endpoint

+

POST http://127.0.0.1:4800/api/transcribe

+

Send a multipart form field named audio containing a WAV file.

+
curl -X POST http://127.0.0.1:4800/api/transcribe \
+  -F "audio=@recording.wav;type=audio/wav"
+
+ +
+

Response

+
{
+  "text": "Hello everyone, here is the complete transcript..."
+}
+

Errors return a JSON object with an error message.

+
+ +
+

Security model

+
    +
  • The API listens on localhost.
  • +
  • CORS is not enabled.
  • +
  • Requests are processed sequentially through the local transcription engine.
  • +
  • Audio files must be WAV and no larger than the app limit.
  • +
+
+
+
+ + diff --git a/website/assets/app-icon.png b/website/assets/app-icon.png new file mode 100644 index 0000000..ce049ca Binary files /dev/null and b/website/assets/app-icon.png differ diff --git a/website/assets/banner.webp b/website/assets/banner.webp new file mode 100644 index 0000000..d205cfd Binary files /dev/null and b/website/assets/banner.webp differ diff --git a/website/assets/logo-hyper.webp b/website/assets/logo-hyper.webp new file mode 100644 index 0000000..00f7aa2 Binary files /dev/null and b/website/assets/logo-hyper.webp differ diff --git a/website/assets/logo-yap.webp b/website/assets/logo-yap.webp new file mode 100644 index 0000000..00f7aa2 Binary files /dev/null and b/website/assets/logo-yap.webp differ diff --git a/website/index.html b/website/index.html new file mode 100644 index 0000000..4059556 --- /dev/null +++ b/website/index.html @@ -0,0 +1,178 @@ + + + + + + HyperYap - Private Local Speech to Text + + + + + + + + + + + + + + + +
+
+
+

Open source desktop dictation

+

HyperYap

+

+ Private local speech-to-text for people who dictate into editors, + terminals, chat apps, issue trackers, and everyday desktop tools. +

+ +
+
+
Processing
+
On-device
+
+
+
Telemetry
+
None
+
+
+
Model
+
Parakeet
+
+
+
+
+ HyperYap desktop voice-to-text banner +
+
+ +
+
+

Workflow

+

Fast dictation without sending audio to a cloud transcription service.

+
+
+
+

Local transcription

+

NVIDIA Parakeet runs on your machine. Audio and transcripts stay local by default.

+
+
+

Record and paste

+

Start recording from a shortcut, stop recording, and paste the result into the active app.

+
+
+

Terminal-aware helpers

+

Windows workstation presets support smart terminal copy, paste, undo, and screenshot path workflows.

+
+
+

Optional cleanup

+

Use local Ollama or a configured endpoint for post-processing only when you enable it.

+
+
+
+ +
+
+
+

Install

+

Desktop builds for Windows, macOS, and Linux.

+

+ HyperYap is distributed through GitHub Releases. The app downloads + its default model on first setup and can run offline after the model + is available. +

+
+ +
+
+ +
+
+

Local API

+

Integrate with the app running on your own machine.

+

+ Enable the experimental local HTTP API in HyperYap settings, then + submit WAV files to localhost. The API is deliberately local-only and + does not enable browser CORS. +

+ Read the API guide +
+
curl -X POST http://127.0.0.1:4800/api/transcribe \
+  -F "audio=@recording.wav;type=audio/wav"
+
+ +
+
+

Privacy boundary

+

No analytics. No telemetry. No uploaded recordings by default.

+
+

+ HyperYap stores settings, custom dictionary entries, and formatting + rules locally. Transcription history is memory-only unless you + explicitly enable persisted history, which stores only the last five + transcriptions. +

+ Privacy details +
+
+ + + + diff --git a/website/llms.txt b/website/llms.txt new file mode 100644 index 0000000..6677c7b --- /dev/null +++ b/website/llms.txt @@ -0,0 +1,16 @@ +# HyperYap + +HyperYap is an open-source desktop speech-to-text application focused on local processing and user privacy. + +Primary URLs: +- Website: https://hyperyap.com/ +- Source: https://github.com/avalonreset/hyperyap +- Releases: https://github.com/avalonreset/hyperyap/releases/latest +- Privacy policy: https://github.com/avalonreset/hyperyap/blob/main/PRIVACY_POLICY.md + +Key facts: +- Speech recognition runs locally on the user's machine by default. +- The project does not include analytics or telemetry. +- Transcription history is memory-only unless the user enables persisted history. +- Persisted history is limited to the last five transcriptions. +- The experimental HTTP API is localhost-only and does not enable CORS. diff --git a/website/privacy.html b/website/privacy.html new file mode 100644 index 0000000..d3b22ab --- /dev/null +++ b/website/privacy.html @@ -0,0 +1,73 @@ + + + + + + HyperYap Privacy + + + + + + + + +
+
+

Privacy

+

Local first by design.

+

+ HyperYap is built to work without sending recordings or transcripts to + a cloud transcription service by default. +

+ +
+

What HyperYap does not do

+
    +
  • It does not include analytics or telemetry.
  • +
  • It does not collect or sell user data.
  • +
  • It does not upload recordings or transcriptions by default.
  • +
+
+ +
+

Local data

+

+ HyperYap stores settings, custom dictionary words, formatting rules, + and optional LLM configuration on your device. API keys are handled + through the operating system keyring where supported. +

+

+ Transcription history is memory-only unless you explicitly enable + persisted history. When enabled, only the last five transcriptions + are saved. +

+
+ +
+

Network usage

+

+ The app may contact GitHub for update checks and downloads. Other + network behavior is opt-in, such as connecting to a local or remote + LLM endpoint you configure, or enabling the localhost HTTP API. +

+
+ +

Read the full project privacy policy

+
+
+ + diff --git a/website/robots.txt b/website/robots.txt new file mode 100644 index 0000000..92032ef --- /dev/null +++ b/website/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Allow: / + +Sitemap: https://hyperyap.com/sitemap.xml diff --git a/website/security.html b/website/security.html new file mode 100644 index 0000000..1180ce0 --- /dev/null +++ b/website/security.html @@ -0,0 +1,59 @@ + + + + + + HyperYap Security + + + + + + + + +
+
+

Security

+

Security-sensitive by default.

+

+ HyperYap handles microphone, clipboard, shortcut, local API, and + optional LLM endpoint behavior. Changes in these areas must preserve + the project privacy and security boundary. +

+ +
+

Important boundaries

+
    +
  • No open CORS for the local API.
  • +
  • No telemetry or hidden analytics.
  • +
  • No remote processing unless explicitly configured by the user.
  • +
  • No plaintext API keys in exported settings files.
  • +
+
+ +
+

Report a vulnerability

+

+ Use the repository security policy for responsible disclosure and + current supported versions. +

+

Open SECURITY.md

+
+
+
+ + diff --git a/website/sitemap.xml b/website/sitemap.xml new file mode 100644 index 0000000..b9a3e0b --- /dev/null +++ b/website/sitemap.xml @@ -0,0 +1,15 @@ + + + + https://hyperyap.com/ + + + https://hyperyap.com/api.html + + + https://hyperyap.com/privacy.html + + + https://hyperyap.com/security.html + + diff --git a/website/styles.css b/website/styles.css new file mode 100644 index 0000000..2daae0d --- /dev/null +++ b/website/styles.css @@ -0,0 +1,453 @@ +:root { + color-scheme: light; + --paper: #f6f3ec; + --ink: #1c2833; + --muted: #5f6c75; + --line: #d7d1c5; + --panel: #fffaf1; + --teal: #0f766e; + --teal-dark: #115e59; + --gold: #d99921; + --coral: #bd4b39; + --sky: #dbeafe; + --shadow: 0 16px 48px rgb(28 40 51 / 12%); +} + +* { + box-sizing: border-box; +} + +html { + scroll-behavior: smooth; +} + +body { + margin: 0; + background: var(--paper); + color: var(--ink); + font-family: + Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", + sans-serif; + line-height: 1.5; +} + +img { + display: block; + max-width: 100%; +} + +a { + color: inherit; +} + +.skip-link { + position: fixed; + left: 1rem; + top: 1rem; + z-index: 10; + transform: translateY(-200%); + background: var(--ink); + color: white; + padding: 0.75rem 1rem; +} + +.skip-link:focus { + transform: translateY(0); +} + +.site-header { + border-bottom: 1px solid var(--line); + background: rgb(246 243 236 / 88%); + backdrop-filter: blur(16px); + position: sticky; + top: 0; + z-index: 5; +} + +.nav, +.site-footer, +.section, +.band-inner, +.hero, +.page-main { + width: min(1180px, calc(100% - 32px)); + margin: 0 auto; +} + +.nav { + min-height: 72px; + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; +} + +.brand, +.nav-links, +.cta-row, +.footer-links { + display: flex; + align-items: center; +} + +.brand { + gap: 0.75rem; + font-weight: 800; + text-decoration: none; +} + +.brand img { + border-radius: 8px; +} + +.nav-links, +.footer-links { + gap: 1rem; + color: var(--muted); + font-size: 0.95rem; +} + +.nav-links a, +.footer-links a, +.text-link { + text-decoration-color: transparent; + text-underline-offset: 0.25em; +} + +.nav-links a:hover, +.footer-links a:hover, +.text-link:hover { + color: var(--teal-dark); + text-decoration-color: currentColor; +} + +.hero { + min-height: min(760px, calc(100vh - 72px)); + display: grid; + grid-template-columns: minmax(0, 0.95fr) minmax(340px, 1.05fr); + gap: clamp(2rem, 4vw, 5rem); + align-items: center; + padding: clamp(3rem, 7vh, 6rem) 0 3rem; +} + +.hero-copy { + max-width: 640px; +} + +.eyebrow { + margin: 0 0 0.8rem; + color: var(--coral); + font-size: 0.78rem; + font-weight: 800; + letter-spacing: 0; + text-transform: uppercase; +} + +h1, +h2, +h3, +p { + margin-top: 0; +} + +h1 { + margin-bottom: 1rem; + font-size: clamp(3.75rem, 8vw, 7rem); + line-height: 0.88; + letter-spacing: 0; +} + +h2 { + margin-bottom: 1rem; + font-size: clamp(2rem, 4vw, 4.25rem); + line-height: 1; + letter-spacing: 0; +} + +h3 { + margin-bottom: 0.75rem; + font-size: 1.25rem; + letter-spacing: 0; +} + +.lede { + color: #30414f; + font-size: clamp(1.1rem, 2vw, 1.4rem); + max-width: 58ch; +} + +.button { + min-height: 48px; + border-radius: 8px; + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0.8rem 1rem; + font-weight: 800; + text-decoration: none; +} + +.button.primary { + background: var(--teal); + color: white; + box-shadow: var(--shadow); +} + +.button.primary:hover { + background: var(--teal-dark); +} + +.button.secondary { + border: 1px solid var(--line); + background: rgb(255 250 241 / 74%); +} + +.cta-row { + flex-wrap: wrap; + gap: 0.75rem; + margin: 2rem 0; +} + +.hero-facts { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 0.75rem; + margin: 0; +} + +.hero-facts div, +.feature-card, +.install-list a, +.privacy-panel, +.content-page { + border: 1px solid var(--line); + background: rgb(255 250 241 / 78%); + box-shadow: 0 8px 28px rgb(28 40 51 / 6%); +} + +.hero-facts div { + border-radius: 8px; + padding: 1rem; +} + +.hero-facts dt { + color: var(--muted); + font-size: 0.8rem; +} + +.hero-facts dd { + margin: 0.15rem 0 0; + font-weight: 900; +} + +.hero-visual { + margin: 0; + overflow: hidden; + border: 1px solid var(--line); + border-radius: 8px; + background: #111827; + box-shadow: var(--shadow); +} + +.hero-visual img { + width: 100%; + aspect-ratio: 16 / 10; + object-fit: cover; +} + +.section { + padding: clamp(4rem, 8vw, 7rem) 0; +} + +.section-heading { + max-width: 850px; + margin-bottom: 2rem; +} + +.feature-grid { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 1rem; +} + +.feature-card { + border-radius: 8px; + min-height: 220px; + padding: 1.25rem; +} + +.feature-card p, +.band p, +.privacy-panel p, +.content-page p, +.content-page li { + color: var(--muted); +} + +.band { + background: + linear-gradient(90deg, rgb(15 118 110 / 12%), transparent 38%), + linear-gradient(180deg, #fffaf1, #f1eadb); + border-block: 1px solid var(--line); +} + +.band-inner { + padding: clamp(4rem, 7vw, 6rem) 0; +} + +.two-column, +.split, +.privacy-panel { + display: grid; + grid-template-columns: minmax(0, 0.95fr) minmax(0, 1.05fr); + gap: clamp(1.5rem, 4vw, 4rem); + align-items: start; +} + +.install-list { + display: grid; + gap: 0.75rem; +} + +.install-list a { + border-radius: 8px; + padding: 1rem; + text-decoration: none; +} + +.install-list a:hover { + border-color: var(--teal); +} + +.install-list span { + display: block; + font-weight: 900; +} + +.install-list small { + color: var(--muted); +} + +.code-panel { + margin: 0; + overflow-x: auto; + border-radius: 8px; + border: 1px solid #263340; + background: #17212b; + color: #e7f7f4; + padding: 1.25rem; + font-size: 0.95rem; +} + +.privacy-panel { + border-radius: 8px; + padding: clamp(1.25rem, 4vw, 2rem); + align-items: center; +} + +.page-main { + padding: clamp(2rem, 6vw, 5rem) 0; +} + +.content-page { + border-radius: 8px; + padding: clamp(1.5rem, 5vw, 3.5rem); +} + +.content-page h1 { + font-size: clamp(3rem, 8vw, 6.5rem); +} + +.content-page section { + border-top: 1px solid var(--line); + margin-top: 2rem; + padding-top: 2rem; +} + +.content-page code { + color: var(--ink); + background: var(--sky); + border-radius: 4px; + padding: 0.1rem 0.3rem; +} + +.content-page pre code { + color: inherit; + background: transparent; + padding: 0; +} + +.site-footer { + border-top: 1px solid var(--line); + padding: 2rem 0; + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; +} + +.site-footer p { + margin: 0.25rem 0 0; + color: var(--muted); +} + +@media (max-width: 900px) { + .hero, + .two-column, + .split, + .privacy-panel { + grid-template-columns: 1fr; + } + + .hero { + min-height: auto; + } + + .feature-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +@media (max-width: 640px) { + .nav { + align-items: flex-start; + flex-direction: column; + padding: 1rem 0; + } + + .nav-links, + .footer-links { + align-items: flex-start; + flex-wrap: wrap; + } + + .feature-grid { + grid-template-columns: 1fr; + } + + .hero { + gap: 2rem; + padding-top: 2rem; + } + + .hero-facts { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + .hero-facts div { + padding: 0.75rem; + } + + .hero-facts dt { + font-size: 0.72rem; + } + + .hero-facts dd { + font-size: 0.92rem; + } + + .site-footer { + align-items: flex-start; + flex-direction: column; + } +}