Compare commits

..

2 commits

Author SHA1 Message Date
b9e56d0825 Merge pull request 'Nightjar brand, hosted service, infrastructure plan, saved site' (#6) from feature/nightjar-brand-hosted-infra into main
Some checks failed
ci / firmware (push) Failing after 30s
ci / openapi (push) Failing after 28s
ci / emulator (push) Failing after 36s
ci / server (push) Failing after 42s
2026-07-05 22:24:28 +01:00
8fd9b694f8 docs+site: Nightjar brand, hosted service, infrastructure plan, saved site
Some checks failed
ci / firmware (pull_request) Failing after 31s
ci / openapi (pull_request) Failing after 33s
ci / emulator (pull_request) Failing after 24s
ci / server (pull_request) Failing after 37s
Persists the branding/commercial/infrastructure work (previously only in chat and
on the live site) into version control.

What changed:
- docs/naming.md: due-diligence and the decision to brand the product Nightjar
  (OpenScribe domain/GitHub/trademark collisions; alternatives screened; why Nightjar).
- docs/hosted-service.md: the commercial model - plan tiers (Self-host / Cloud
  Starter / Cloud Pro / Private), indicative pricing, backend mapping, and the
  build-to-sell roadmap (metering, billing, provisioning, DPA).
- docs/infrastructure.md: backend infra plan. Primary option = self-host on the
  3x Minisforum MS-02 cluster (one with an RTX 3050 6GB) fronted by a Cloudflare
  Tunnel, with node roles, the 3050 capacity reality, caveats, and a Hetzner cloud
  fallback. Provision-later.
- site/: reproducible marketing-site source - block content for all 11 pages
  (rebranded to Nightjar), the navigation, the Contact Form 7 config (honeypot),
  the ApisCP SOAP helper (tools/apiscp.php, no secret), and a README on how the
  WordPress site is built and managed via the API.
- state/: DECISIONS (Nightjar rebrand, hosted service, MS-02 backend), PROJECT
  (brand + commercial section), TODO (rename decision, trademark, mailbox, pricing,
  hosted-service build, infra provisioning).

Why:
- User asked to save everything to the repo. Captures the product rebrand, the
  commercialisation plan, and the infrastructure decision so a cold session has the
  full picture.

Notes:
- The repo is still named `openscribe`; the product/brand is Nightjar. A full
  codebase rename is deferred (tracked in TODO + docs/naming.md).
- No secrets committed: the ApisCP API key is read from a local scratch file, never
  the repo.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 22:24:24 +01:00
21 changed files with 592 additions and 0 deletions

43
docs/hosted-service.md Normal file
View file

@ -0,0 +1,43 @@
<!-- SPDX-License-Identifier: CC-BY-SA-4.0 -->
# Nightjar hosted service (commercial)
How 12 Hobbies Studio commercialises the open-source project: a managed backend so people
can use Nightjar without self-hosting. The device and app stay open; the studio runs the
transcription, summaries, storage and API.
Status: **marketing site live, service in preparation.** No billing/signup yet; the site
captures leads (register interest). Pricing below is **indicative**.
## Plan lineup (live on the site)
| Plan | Price (indicative) | AI backend | Notes |
|------|--------------------|-----------|-------|
| **Self-host** | Free, forever | your own | full source + docs, community support |
| **Cloud Starter** | from GBP 6/mo | commercial API | 10 hrs audio/mo, 10 GB, email support |
| **Cloud Pro** | from GBP 18/mo | commercial API | 40 hrs/mo, priority, 100 GB, team sharing |
| **Private** | from GBP 39/mo | self-hosted open models | audio never touches a 3rd-party AI; DPA |
The Cloud tiers use best-in-class commercial AI (cheap per-minute, good margin). The
Private tier processes everything on our own hardware (see `docs/infrastructure.md`) for a
privacy / sovereignty guarantee. All hosted data kept in the EU/UK.
## Placeholders to confirm before launch
- **Contact mailbox:** the site's forms/CTAs use `hello@12hobbies.studio` - create that
mailbox or change it.
- **Real pricing + quotas:** the numbers above are indicative.
- **Trademark clearance** for "Nightjar" (see `docs/naming.md`).
## Roadmap to actually sell it (not built yet)
- Metering (hours of audio, storage) per account.
- Signup + billing (e.g. Stripe) wired to plan entitlements.
- Provisioning: create a tenant, route jobs to the right backend (commercial vs Private
node), enforce quotas.
- Provision the backend infra (`docs/infrastructure.md`) at first beta / paying users.
- Data-processing agreement + retention policy for the Private tier.
## Marketing site
Built in WordPress on ApisCP (`howler.qsplace.co.uk`), served at
`nightjar.12hobbies.studio`. Reproducible content and build notes are in `site/`.

103
docs/infrastructure.md Normal file
View file

@ -0,0 +1,103 @@
<!-- SPDX-License-Identifier: CC-BY-SA-4.0 -->
# Nightjar hosted backend - infrastructure plan
Plan for the infrastructure that runs the **hosted** Nightjar service (the commercial
plans). The open-source project itself does not require any of this; self-hosters run
their own. This is for 12 Hobbies Studio to operate the managed service.
Status: **plan only, not provisioned** (decided 2026-07). Stand it up at first beta /
paying users. See `docs/hosted-service.md` for the plan tiers this maps to.
## Two backends, matching the two plan families
| Plan family | AI runs on | Infra |
|-------------|-----------|-------|
| **Cloud Starter / Pro** | commercial API (Groq / OpenAI / Anthropic) | small orchestration node; heavy compute offloaded |
| **Private** | self-hosted open models (faster-whisper + local LLM) | a GPU node; audio never leaves our hardware |
Cloud tiers are cheap to run (compute is per-minute on a commercial provider, keeping the
GBP 6 / 18 pricing profitable). The Private tier needs real hardware but is the privacy /
sovereignty story.
## Primary option: self-host on the MS-02 cluster (best for small scale)
Hardware on hand: **3x Minisforum MS-02-class mini-workstations**, one fitted with an
**NVIDIA RTX 3050 6GB**. High-core CPUs, dual 10GbE, NVMe, PCIe GPU slot. This is a strong
small-scale backend and a better fit for the Private tier than rented cloud.
### Node roles
- **Node A (RTX 3050) - AI worker.** faster-whisper on the GPU + a local LLM (Ollama) for
summaries. This is the Private-tier engine; models run on our own hardware.
- **Node B - app tier.** FastAPI ingest + open API, Postgres (accounts, metadata,
billing), Redis queue, reverse proxy.
- **Node C - storage + overflow.** MinIO (S3-compatible object storage for audio), a CPU
transcription worker for extra throughput, backups, and HA spare.
Dual 10GbE makes shared storage and job distribution between the three effortless. Run
Docker Compose per node, or k3s / Docker Swarm across all three for failover.
### The RTX 3050 6GB in practice
- **Transcription: excellent.** faster-whisper (CTranslate2, int8) runs whisper
medium/large-v3 within 6GB and several times faster than real-time - hundreds of hours
of audio per day on one GPU. Far beyond small-scale need.
- **Summaries: fine with 7-8B models.** A quantised 8B model (Llama 3.1 8B, Qwen2.5 7B,
Mistral 7B at Q4) fits in 6GB and writes good summaries in seconds. 13B+ will not fit in
VRAM; for bigger, use CPU (slower) or add a larger GPU later. Not needed for summaries.
So the single 3050 covers both AI jobs the Private tier needs. Cloud tiers barely touch
this hardware (orchestration + storage only).
### Getting to it from the internet: Cloudflare Tunnel
Do not expose the home/office IP or open ports. We are already on Cloudflare - run a
**Cloudflare Tunnel**: the nodes make an outbound connection and `api.nightjar...` is served
through Cloudflare with TLS and DDoS protection, no inbound ports, no static IP. Free.
### Caveats of self-hosting a paid service
- **Uptime / power / bandwidth.** Fine for beta / early customers. Add a UPS, decent
upload bandwidth, tunnel resilience. For SLA-bound customers later, consider colo or a
hybrid.
- **Backups.** NVMe fails. Keep offsite backups of Postgres + audio (cheap cloud object
storage target).
- **Data residency.** On our own UK premises - a strong Private-tier pitch ("processed on
our own hardware in the UK, never sent to a third-party AI"). Mind UK GDPR obligations.
- **When we outgrow it.** Many concurrent Private customers, or wanting large LLMs - add
VRAM / a second GPU, or a cloud GPU node.
## Fallback / alternative option: cloud VMs (Hetzner)
If self-hosting uptime is a concern, or to launch before the cluster is ready:
- **Phase 1 (Cloud tiers):** one small Hetzner VM (CPX31, 4 vCPU / 8 GB, ~EUR 15/mo, EU
region for GDPR). Docker Compose: FastAPI + workers + Redis + Postgres + Caddy/LE.
Object storage via Hetzner Object Storage (~EUR 5/mo). AI offloaded to a commercial API.
All-in ~EUR 20-30/mo.
- **Phase 2 (Private tier):** a CPU-optimised (CCX23/33, ~EUR 30-60/mo) or GPU VM for
self-hosted whisper + LLM. Priced into the Private tier.
A hybrid also works: MS-02 cluster does the compute; a tiny cloud VM (or just the
Cloudflare Tunnel) is the public front door.
## Architecture (both options)
```
device --sync--> ingest API --> queue --> transcription worker --> summary (LLM)
| (GPU or commercial) (local or commercial)
v
Postgres (metadata/accounts) object storage (audio + artefacts)
|
open REST API + exports <-- app / customers
```
## Provisioning checklist (when ready)
- [ ] Decide compute home: MS-02 cluster (primary) vs cloud (fallback) vs hybrid.
- [ ] Cloudflare Tunnel for `api.nightjar...` (if self-hosting).
- [ ] Docker Compose (or k3s): FastAPI, workers, Redis, Postgres, MinIO/Object Storage.
- [ ] GPU node: faster-whisper (CUDA) + Ollama (7-8B) for the Private tier.
- [ ] Backups (Postgres + audio) offsite; UPS on the nodes.
- [ ] Metering + billing hookup for the plans (see `docs/hosted-service.md`).
- [ ] Data-processing agreement + retention policy (Private tier).

52
docs/naming.md Normal file
View file

@ -0,0 +1,52 @@
<!-- SPDX-License-Identifier: CC-BY-SA-4.0 -->
# Naming and branding
Record of the naming due-diligence and the decision to brand the product **Nightjar**.
Reviewed July 2026.
## Decision
- **Product / commercial brand: Nightjar.** The site is live at
`https://nightjar.12hobbies.studio`.
- **The repository is still named `openscribe`** for now. Renaming the whole codebase
(repo, SPDX headers, firmware namespaces, docs) is a separate, larger task - deferred
until decided. "OpenScribe" survives only as the original project name in code history.
## Why not "OpenScribe"
"OpenScribe" failed a due-diligence screen for a commercial brand:
- **Domains:** every desirable exact-match is taken. `openscribe.com` (parked, for sale
since 2009), `openscribe.ai` (parked, for sale), `.net/.org/.app/.dev` registered,
`.org` is an active dictation product, `.io`/`.co` host live sites.
- **GitHub:** 33 repos match "openscribe", several in the same category (an AI medical
scribe at ~188 stars, dictation apps, transcription tools). Heavy in-category collision.
- **Trademark:** descriptive ("Open" + "Scribe"), so weak/hard to register, and there is
at least one live commercial product on the exact name in the same class. Not cleared.
## Why "Nightjar"
- A nightjar is a bird with a famously distinctive call - evocative, memorable, and it
does **not** collide inside the AI/voice/transcription category (the trap OpenScribe hit).
- GitHub name collisions: ~104 (vs 5,000+ for the coined alternative "Recita", and far
lower than dictionary words like murmur/cadence/quill).
- As a bird name applied to AI software it is an **arbitrary mark** - a strong, defensible
trademark position (unrelated to the goods).
## Names screened and rejected
- **Mockingjay** - Hunger Games / Lionsgate trademark, aggressively enforced. Hard avoid.
- **Lyrebird, Fathom** - already established voice-AI / AI-notetaker brands (in-category
traps like OpenScribe).
- **Murmur, Cadence, Quill, Cairn, Keepsake** - dictionary words: taken domains + heavy
GitHub collisions.
- **Recita** - coined but turns out common (Italian word), ~5,000 GitHub matches.
## Caveats / follow-ups
- Every short name has its exact-match `.com` squatted; Nightjar is no exception. Domain
routes: `getnightjar.com` / a modern TLD if free at a registrar / the studio subdomain
(current: `nightjar.12hobbies.studio`).
- A **formal trademark clearance** (USPTO / UK IPO / EUIPO, ideally via an IP attorney) is
the last step before spending on the brand. This screen is not a legal clearance.

58
site/README.md Normal file
View file

@ -0,0 +1,58 @@
# Nightjar marketing site
The content and tooling for the public marketing site at
**https://nightjar.12hobbies.studio** (the commercial front for the project; see
`../docs/hosted-service.md`).
The live site is **WordPress on ApisCP** (`howler.qsplace.co.uk`, EU/Hetzner), on the
`12hobbies` account as a subdomain. This directory is the reproducible source: the page
content, navigation, contact-form config, and the tooling used to push it all through the
ApisCP API.
## Layout
```
pages/ Block-editor content for each page (one file per page)
home.html front page (landing)
how-it-works.html workflow + architecture diagrams (inline SVG) + detailed steps
bring-your-own-ai.html
guides.html hub linking the three tutorials
connect-your-ai.html tutorial: per-provider .env config
self-host-the-server.html tutorial: install -> configure -> run
build-the-device.html tutorial: parts, flashing, case (+ device illustration)
open-source.html hosted-plans.html about.html contact.html
_navigation.html the header navigation (wp_navigation block content)
cf7-form-config.php Contact Form 7 form template + mail config (honeypot spam protection)
tools/apiscp.php SOAP helper that drives ApisCP / wp-cli remotely (see below)
```
Images are **inline SVG** inside Custom HTML blocks (no uploads, no SVG-upload security
concerns; the device illustration has an animated status light).
## How the site is managed
WordPress is driven remotely through the ApisCP SOAP API via `tools/apiscp.php`, which
exposes `wordpress_cli` (wp-cli) and file operations. Page content is pushed to a temp
file on the server and applied with `wp post update <id> <file>` (avoids command-length
and quoting limits). Example:
```
php -d extension=openssl -d extension=soap tools/apiscp.php \
file_put_file_contents /var/www/<docroot>/.tmp.html '@@pages/home.html' true false
php -d extension=openssl -d extension=soap tools/apiscp.php \
wordpress_cli 'post update <id> /var/www/<docroot>/.tmp.html' '[]' nightjar.12hobbies.studio ''
```
### Secrets
`tools/apiscp.php` reads the ApisCP API key from a local scratch file
(`C:/temp/claudetemp/apiscp.key`) that is **never committed**. Supply the key out-of-band
and keep it out of the repo. The `.gitignore` already excludes secrets.
## Site facts
- Comments are disabled site-wide (Disable Comments plugin + closed defaults).
- The contact form is Contact Form 7 with a hidden **honeypot** field for spam protection;
every submission is also stored in the database via Flamingo.
- TLS is Let's Encrypt (valid cert for `nightjar.12hobbies.studio`). The old
`openscribe.12hobbies.studio` host redirects here.

29
site/cf7-form-config.php Normal file
View file

@ -0,0 +1,29 @@
<?php
// Configure the default Contact Form 7 form: friendly fields, a honeypot field for spam
// protection, and the recipient. Flamingo separately stores every submission in the DB.
$form_id = 16;
if (!function_exists('wpcf7_contact_form')) { echo "cf7-missing\n"; return; }
$cf = wpcf7_contact_form($form_id);
if (!$cf) { echo "no-form\n"; return; }
$p = $cf->get_properties();
$p['form'] = implode("\n", array(
'<p>Your name<br />[text* your-name autocomplete:name]</p>',
'<p>Your email<br />[email* your-email autocomplete:email]</p>',
'<p>I am interested in<br />[select interest "Hosted plans" "Early access to the code" "Building a device" "Something else"]</p>',
'<p>Message<br />[textarea* your-message]</p>',
'[honeypot website-hp id:website-hp nomessage:true]',
'<p>[submit "Send message"]</p>',
));
$p['mail']['recipient'] = 'hello@12hobbies.studio';
$p['mail']['sender'] = 'Nightjar <wordpress@nightjar.12hobbies.studio>';
$p['mail']['subject'] = 'Nightjar enquiry from [your-name]';
$p['mail']['additional_headers'] = 'Reply-To: [your-email]';
$p['mail']['body'] = "Name: [your-name]\nEmail: [your-email]\nInterested in: [interest]\n\nMessage:\n[your-message]\n\n-- Sent from nightjar.12hobbies.studio";
$cf->set_properties($p);
$cf->save();
echo $cf->shortcode() . "\n";

View file

@ -0,0 +1 @@
<!-- wp:navigation-link {"label":"Home","url":"https://nightjar.12hobbies.studio/","kind":"custom","isTopLevelLink":true} /--><!-- wp:navigation-link {"label":"How it works","url":"https://nightjar.12hobbies.studio/how-it-works/","kind":"custom","isTopLevelLink":true} /--><!-- wp:navigation-link {"label":"Guides","url":"https://nightjar.12hobbies.studio/guides/","kind":"custom","isTopLevelLink":true} /--><!-- wp:navigation-link {"label":"Bring your own AI","url":"https://nightjar.12hobbies.studio/bring-your-own-ai/","kind":"custom","isTopLevelLink":true} /--><!-- wp:navigation-link {"label":"Hosted plans","url":"https://nightjar.12hobbies.studio/hosted-plans/","kind":"custom","isTopLevelLink":true} /--><!-- wp:navigation-link {"label":"Open source","url":"https://nightjar.12hobbies.studio/open-source/","kind":"custom","isTopLevelLink":true} /--><!-- wp:navigation-link {"label":"About","url":"https://nightjar.12hobbies.studio/about/","kind":"custom","isTopLevelLink":true} /--><!-- wp:navigation-link {"label":"Contact","url":"https://nightjar.12hobbies.studio/contact/","kind":"custom","isTopLevelLink":true} /-->

1
site/pages/about.html Normal file
View file

@ -0,0 +1 @@
<!-- wp:heading {"level":1} --><h1 class="wp-block-heading">About</h1><!-- /wp:heading --><!-- wp:paragraph {"fontSize":"large"} --><p class="has-large-font-size">Nightjar is a project by 12 Hobbies Studio, a maker studio that builds open, self-hostable tools.</p><!-- /wp:paragraph --><!-- wp:paragraph --><p>12 Hobbies Studio makes small, open-source projects across hardware and software. The through-line is simple: build in the open, keep the data with the person who made it, and let anyone reproduce the whole thing from the repository alone. Firmware and software are copyleft, hardware is open-hardware licensed, and documentation is share-alike.</p><!-- /wp:paragraph --><!-- wp:paragraph --><p>Nightjar is the studio flagship: an AI voice recorder that gives you the convenience people expect from a commercial device, without giving up ownership of your hardware, your recordings, or your choice of AI. The hosted plans exist so the studio can sustainably maintain and grow the open project while offering a managed option to anyone who wants it.</p><!-- /wp:paragraph --><!-- wp:heading --><h2 class="wp-block-heading">Get in touch</h2><!-- /wp:heading --><!-- wp:paragraph --><p>Questions, ideas, or interest in a hosted plan or early access to the code? We would love to hear from you.</p><!-- /wp:paragraph --><!-- wp:buttons --><div class="wp-block-buttons"><!-- wp:button --><div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="/contact/">Contact us</a></div><!-- /wp:button --></div><!-- /wp:buttons --><!-- wp:paragraph {"fontSize":"small"} --><p class="has-small-font-size">Nightjar is an independent open-source project inspired by the Plaud class of device. It is not affiliated with, endorsed by, or derived from Plaud.</p><!-- /wp:paragraph -->

View file

@ -0,0 +1 @@
<!-- wp:heading {"level":1} --><h1 class="wp-block-heading">Bring your own AI</h1><!-- /wp:heading --><!-- wp:paragraph {"fontSize":"large"} --><p class="has-large-font-size">This is the difference. A commercial recorder locks you to its cloud. Nightjar lets you point transcription and summaries at whatever you want.</p><!-- /wp:paragraph --><!-- wp:paragraph --><p>Transcription and summarisation each target a provider you choose in configuration. Use a fully local model for maximum privacy, an open-standard endpoint, or a commercial API for the best quality. There is no lock-in, and you can switch at any time. Self-hosted or not, it is your choice and your data.</p><!-- /wp:paragraph --><!-- wp:heading --><h2 class="wp-block-heading">Language and summaries</h2><!-- /wp:heading --><!-- wp:list --><ul class="wp-block-list"><!-- wp:list-item --><li><strong>OpenAI</strong>, or any OpenAI-compatible endpoint</li><!-- /wp:list-item --><!-- wp:list-item --><li><strong>Anthropic Claude</strong></li><!-- /wp:list-item --><!-- wp:list-item --><li><strong>Groq</strong> for fast, low-cost inference</li><!-- /wp:list-item --><!-- wp:list-item --><li><strong>Local models</strong> via Ollama or LM Studio, fully offline</li><!-- /wp:list-item --><!-- wp:list-item --><li><strong>Gemini</strong> through its OpenAI-compatible endpoint</li><!-- /wp:list-item --></ul><!-- /wp:list --><!-- wp:heading --><h2 class="wp-block-heading">Transcription</h2><!-- /wp:heading --><!-- wp:list --><ul class="wp-block-list"><!-- wp:list-item --><li><strong>faster-whisper</strong>, self-hosted, running on your own machine</li><!-- /wp:list-item --><!-- wp:list-item --><li>Any OpenAI-compatible audio endpoint, such as OpenAI Whisper or Groq</li><!-- /wp:list-item --><!-- wp:list-item --><li>Your own whisper server</li><!-- /wp:list-item --></ul><!-- /wp:list --><!-- wp:heading --><h2 class="wp-block-heading">How it is configured</h2><!-- /wp:heading --><!-- wp:paragraph --><p>Each provider is a few lines of configuration on the server: a provider name, a base URL, an API key and a model. Almost every provider speaks the same OpenAI-compatible protocol, so a single setting covers most of the ecosystem. Anthropic Claude has its own dedicated integration. Pick a provider, drop in the details, and Nightjar does the rest.</p><!-- /wp:paragraph --><!-- wp:buttons --><div class="wp-block-buttons"><!-- wp:button --><div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="/open-source/">Explore the project</a></div><!-- /wp:button --><!-- wp:button {"className":"is-style-outline"} --><div class="wp-block-button is-style-outline"><a class="wp-block-button__link wp-element-button" href="/hosted-plans/">Prefer a managed backend</a></div><!-- /wp:button --></div><!-- /wp:buttons -->

View file

@ -0,0 +1,26 @@
<!-- wp:heading {"level":1} --><h1 class="wp-block-heading">Build the device</h1><!-- /wp:heading --><!-- wp:paragraph {"fontSize":"large"} --><p class="has-large-font-size">The Nightjar recorder is built from off-the-shelf parts and a printed case. No custom circuit board required. Here is what goes into one.</p><!-- /wp:paragraph -->
<!-- wp:html -->
<figure style="margin:0 auto 8px;max-width:280px">
<svg viewBox="0 0 240 300" width="100%" role="img" aria-label="Nightjar recorder illustration" xmlns="http://www.w3.org/2000/svg" style="font-family:ui-monospace,monospace">
<defs>
<linearGradient id="bd" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#3f3f46"/><stop offset="1" stop-color="#18181b"/></linearGradient>
<radialGradient id="lg" cx="0.5" cy="0.5" r="0.5"><stop offset="0" stop-color="#5eead4"/><stop offset="1" stop-color="#5eead4" stop-opacity="0"/></radialGradient>
</defs>
<rect x="70" y="30" width="100" height="240" rx="46" fill="url(#bd)" stroke="#52525b" stroke-width="2"/>
<rect x="86" y="48" width="12" height="200" rx="6" fill="#ffffff" opacity="0.06"/>
<g fill="#71717a"><circle cx="108" cy="80" r="3.5"/><circle cx="120" cy="74" r="3.5"/><circle cx="132" cy="80" r="3.5"/><circle cx="108" cy="94" r="3.5"/><circle cx="120" cy="88" r="3.5"/><circle cx="132" cy="94" r="3.5"/></g>
<circle cx="120" cy="130" r="18" fill="url(#lg)"><animate attributeName="opacity" values="0.4;1;0.4" dur="2.6s" repeatCount="indefinite"/></circle>
<circle cx="120" cy="130" r="5" fill="#99f6e4"/>
<text x="120" y="182" text-anchor="middle" font-size="11" letter-spacing="2" fill="#a1a1aa">NIGHTJAR</text>
<circle cx="120" cy="226" r="24" fill="#0c0a09" stroke="#52525b" stroke-width="2"/>
<circle cx="120" cy="226" r="17" fill="#e08a3c"/>
</svg>
</figure>
<!-- /wp:html -->
<!-- wp:heading --><h2 class="wp-block-heading">The parts</h2><!-- /wp:heading --><!-- wp:list --><ul class="wp-block-list"><!-- wp:list-item --><li><strong>An ESP32-S3 board with PSRAM.</strong> This is the brain: it has WiFi, Bluetooth and enough memory to buffer audio. A compact board with an onboard microphone and card slot keeps the whole thing tiny.</li><!-- /wp:list-item --><!-- wp:list-item --><li><strong>A microphone.</strong> A digital I2S MEMS microphone for clean, low-noise audio.</li><!-- /wp:list-item --><!-- wp:list-item --><li><strong>A microSD card.</strong> Where recordings are written. 16 to 32 GB is plenty.</li><!-- /wp:list-item --><!-- wp:list-item --><li><strong>A small LiPo battery</strong> and a charging circuit, so it runs off the mains or on the go.</li><!-- /wp:list-item --><!-- wp:list-item --><li><strong>A button and a status light</strong> for control and feedback.</li><!-- /wp:list-item --></ul><!-- /wp:list --><!-- wp:paragraph --><p>A full bill of materials with exact parts and wiring ships with the project. Indicative cost is under thirty pounds, plus a printed case.</p><!-- /wp:paragraph -->
<!-- wp:heading --><h2 class="wp-block-heading">Step 1: Flash the firmware</h2><!-- /wp:heading --><!-- wp:paragraph --><p>The firmware is built with PlatformIO. Connect the board over USB and flash it.</p><!-- /wp:paragraph --><!-- wp:code --><pre class="wp-block-code"><code>cd nightjar/firmware
pio run -t upload
pio device monitor</code></pre><!-- /wp:code --><!-- wp:paragraph --><p>The serial monitor confirms the board booted, found its microphone and card, and is ready to record.</p><!-- /wp:paragraph -->
<!-- wp:heading --><h2 class="wp-block-heading">Step 2: Print the case</h2><!-- /wp:heading --><!-- wp:paragraph --><p>The enclosure is a parametric model you can print in a couple of hours. Because it is parametric, you can re-tune it to the exact board and battery you chose, then print the two halves and snap them together around the electronics.</p><!-- /wp:paragraph -->
<!-- wp:heading --><h2 class="wp-block-heading">Step 3: Provision and record</h2><!-- /wp:heading --><!-- wp:paragraph --><p>Open the app, connect to the device over Bluetooth, and give it your WiFi details and your server address. Press the button to record. Put it on charge, and it uploads on its own. That is the whole loop.</p><!-- /wp:paragraph -->
<!-- wp:paragraph --><p>Want the software side first? See <a href="/self-host-the-server/">self-host the server</a> and <a href="/connect-your-ai/">connect your AI</a>. Or skip the build entirely with a <a href="/hosted-plans/">hosted plan</a>.</p><!-- /wp:paragraph -->

View file

@ -0,0 +1,24 @@
<!-- wp:heading {"level":1} --><h1 class="wp-block-heading">Connect your AI</h1><!-- /wp:heading --><!-- wp:paragraph {"fontSize":"large"} --><p class="has-large-font-size">Nightjar does not hard-wire a single AI vendor. Transcription and summaries each target a provider you choose in the server config. This guide shows the exact settings for each.</p><!-- /wp:paragraph -->
<!-- wp:paragraph --><p>All settings live in the server&#8217;s <code>.env</code> file. Change them, restart the server, and the new provider is live. Nothing else needs to change on the device or in the app.</p><!-- /wp:paragraph -->
<!-- wp:heading --><h2 class="wp-block-heading">Summaries and chat (the language model)</h2><!-- /wp:heading --><!-- wp:paragraph --><p>Set <code>LLM_PROVIDER</code> to one of <code>ollama</code>, <code>openai_compatible</code>, or <code>anthropic</code>.</p><!-- /wp:paragraph -->
<!-- wp:heading {"level":3} --><h3 class="wp-block-heading">Fully local (Ollama)</h3><!-- /wp:heading --><!-- wp:paragraph --><p>The default. Everything stays on your machine, no API key, no cloud. Install <a href="https://ollama.com">Ollama</a>, pull a model, and point Nightjar at it.</p><!-- /wp:paragraph --><!-- wp:code --><pre class="wp-block-code"><code>NIGHTJAR_LLM_PROVIDER=ollama
NIGHTJAR_OLLAMA_URL=http://localhost:11434
NIGHTJAR_OLLAMA_MODEL=llama3.1</code></pre><!-- /wp:code -->
<!-- wp:heading {"level":3} --><h3 class="wp-block-heading">OpenAI, or anything OpenAI-compatible</h3><!-- /wp:heading --><!-- wp:paragraph --><p>One setting covers OpenAI, Groq, OpenRouter, Together, LocalAI, vLLM, LM Studio, and Gemini&#8217;s OpenAI-compatible endpoint. Just change the base URL and model.</p><!-- /wp:paragraph --><!-- wp:code --><pre class="wp-block-code"><code>NIGHTJAR_LLM_PROVIDER=openai_compatible
NIGHTJAR_LLM_BASE_URL=https://api.openai.com/v1
NIGHTJAR_LLM_API_KEY=sk-...
NIGHTJAR_LLM_MODEL=gpt-4o-mini</code></pre><!-- /wp:code --><!-- wp:paragraph --><p>For Groq (fast and cheap), use <code>https://api.groq.com/openai/v1</code>. For a local model server on your LAN such as LM Studio, use <code>http://localhost:1234/v1</code>.</p><!-- /wp:paragraph -->
<!-- wp:heading {"level":3} --><h3 class="wp-block-heading">Anthropic Claude</h3><!-- /wp:heading --><!-- wp:paragraph --><p>Claude uses its own integration.</p><!-- /wp:paragraph --><!-- wp:code --><pre class="wp-block-code"><code>NIGHTJAR_LLM_PROVIDER=anthropic
NIGHTJAR_LLM_API_KEY=sk-ant-...
NIGHTJAR_LLM_MODEL=claude-opus-4-8</code></pre><!-- /wp:code -->
<!-- wp:heading --><h2 class="wp-block-heading">Transcription (speech to text)</h2><!-- /wp:heading --><!-- wp:paragraph --><p>Set <code>TRANSCRIPTION_PROVIDER</code> to <code>local_whisper</code> (self-hosted) or <code>openai_compatible</code>.</p><!-- /wp:paragraph -->
<!-- wp:heading {"level":3} --><h3 class="wp-block-heading">Local, self-hosted (faster-whisper)</h3><!-- /wp:heading --><!-- wp:code --><pre class="wp-block-code"><code>NIGHTJAR_TRANSCRIPTION_PROVIDER=local_whisper
NIGHTJAR_WHISPER_MODEL=base
NIGHTJAR_WHISPER_DEVICE=cpu
NIGHTJAR_WHISPER_COMPUTE_TYPE=int8</code></pre><!-- /wp:code --><!-- wp:paragraph --><p>Larger models (<code>small</code>, <code>medium</code>, <code>large-v3</code>) are more accurate but slower. Set <code>DEVICE=cuda</code> if you have a GPU.</p><!-- /wp:paragraph -->
<!-- wp:heading {"level":3} --><h3 class="wp-block-heading">A hosted transcription API</h3><!-- /wp:heading --><!-- wp:code --><pre class="wp-block-code"><code>NIGHTJAR_TRANSCRIPTION_PROVIDER=openai_compatible
NIGHTJAR_TRANSCRIPTION_BASE_URL=https://api.openai.com/v1
NIGHTJAR_TRANSCRIPTION_API_KEY=sk-...
NIGHTJAR_TRANSCRIPTION_MODEL=whisper-1</code></pre><!-- /wp:code -->
<!-- wp:heading --><h2 class="wp-block-heading">Which should I choose?</h2><!-- /wp:heading --><!-- wp:list --><ul class="wp-block-list"><!-- wp:list-item --><li><strong>Maximum privacy, zero cost per minute:</strong> local Whisper + a local Ollama model. Nothing leaves your machine.</li><!-- /wp:list-item --><!-- wp:list-item --><li><strong>Best quality, least setup:</strong> a commercial API for both. You pay per use, and accuracy is top-tier.</li><!-- /wp:list-item --><!-- wp:list-item --><li><strong>A balance:</strong> local transcription for privacy, a commercial model for the summary polish.</li><!-- /wp:list-item --></ul><!-- /wp:list -->
<!-- wp:paragraph --><p>Not sure, or would rather not manage any of this? <a href="/hosted-plans/">Our hosted plans</a> run it all for you.</p><!-- /wp:paragraph --><!-- wp:buttons --><div class="wp-block-buttons"><!-- wp:button --><div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="/self-host-the-server/">Next: self-host the server</a></div><!-- /wp:button --></div><!-- /wp:buttons -->

1
site/pages/contact.html Normal file
View file

@ -0,0 +1 @@
<!-- wp:heading {"level":1} --><h1 class="wp-block-heading">Contact</h1><!-- /wp:heading --><!-- wp:paragraph {"fontSize":"large"} --><p class="has-large-font-size">Register your interest in a hosted plan, or ask to be notified when the open-source code is released.</p><!-- /wp:paragraph --><!-- wp:paragraph --><p>Tell us a little about what you are after. If it is a hosted plan, a rough idea of how many hours of audio you record per month helps us follow up with early-access pricing. We read every message.</p><!-- /wp:paragraph --><!-- wp:shortcode -->[contact-form-7 id="55b5907" title="Contact form 1"]<!-- /wp:shortcode --><!-- wp:paragraph {"fontSize":"small"} --><p class="has-small-font-size">Built by 12 Hobbies Studio. Nightjar is an independent open-source project and is not affiliated with Plaud.</p><!-- /wp:paragraph -->

7
site/pages/guides.html Normal file
View file

@ -0,0 +1,7 @@
<!-- wp:heading {"level":1} --><h1 class="wp-block-heading">Guides</h1><!-- /wp:heading --><!-- wp:paragraph {"fontSize":"large"} --><p class="has-large-font-size">Everything you need to run Nightjar yourself: connect an AI provider, stand up the server, and build the device. Start anywhere.</p><!-- /wp:paragraph -->
<!-- wp:columns --><div class="wp-block-columns">
<!-- wp:column --><div class="wp-block-column"><!-- wp:heading {"level":3} --><h3 class="wp-block-heading">Connect your AI</h3><!-- /wp:heading --><!-- wp:paragraph --><p>Point transcription and summaries at a local model, an open-standard endpoint, or a commercial API. Copy-paste config for each.</p><!-- /wp:paragraph --><!-- wp:paragraph --><p><a href="/connect-your-ai/">Read the guide &rarr;</a></p><!-- /wp:paragraph --></div><!-- /wp:column -->
<!-- wp:column --><div class="wp-block-column"><!-- wp:heading {"level":3} --><h3 class="wp-block-heading">Self-host the server</h3><!-- /wp:heading --><!-- wp:paragraph --><p>Stand up the Nightjar backend on your own machine or a small cloud box, from install to first transcript.</p><!-- /wp:paragraph --><!-- wp:paragraph --><p><a href="/self-host-the-server/">Read the guide &rarr;</a></p><!-- /wp:paragraph --></div><!-- /wp:column -->
<!-- wp:column --><div class="wp-block-column"><!-- wp:heading {"level":3} --><h3 class="wp-block-heading">Build the device</h3><!-- /wp:heading --><!-- wp:paragraph --><p>The parts, the firmware, and the printed case. From a bag of components to your first recording.</p><!-- /wp:paragraph --><!-- wp:paragraph --><p><a href="/build-the-device/">Read the guide &rarr;</a></p><!-- /wp:paragraph --></div><!-- /wp:column -->
</div><!-- /wp:columns -->
<!-- wp:paragraph --><p>New here? Start with <a href="/how-it-works/">How Nightjar works</a> for the big picture, or if you would rather skip the setup entirely, see <a href="/hosted-plans/">hosted plans</a>.</p><!-- /wp:paragraph -->

1
site/pages/home.html Normal file
View file

@ -0,0 +1 @@
<!-- wp:heading {"level":1} --><h1 class="wp-block-heading">Nightjar</h1><!-- /wp:heading --><!-- wp:paragraph {"fontSize":"large"} --><p class="has-large-font-size">An open-source, self-hosted AI voice recorder. Record on a small device, sync to your own server, and turn conversations into transcripts and summaries with any AI you choose. Own the hardware, own the data, own the workflow.</p><!-- /wp:paragraph --><!-- wp:buttons --><div class="wp-block-buttons"><!-- wp:button --><div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="/hosted-plans/">See hosted plans</a></div><!-- /wp:button --><!-- wp:button {"className":"is-style-outline"} --><div class="wp-block-button is-style-outline"><a class="wp-block-button__link wp-element-button" href="/open-source/">The open-source project</a></div><!-- /wp:button --></div><!-- /wp:buttons --><!-- wp:separator --><hr class="wp-block-separator has-alpha-channel-opacity"/><!-- /wp:separator --><!-- wp:heading --><h2 class="wp-block-heading">An AI note-taker you actually own</h2><!-- /wp:heading --><!-- wp:paragraph --><p>Nightjar is a pocket voice recorder in the spirit of the AI note-takers you have seen, rebuilt as open hardware and open software. A small ESP32-S3 device records to a memory card at the press of a button. Recordings sync to a mobile app and to a server you control, where they become searchable transcripts and structured summaries. No sealed box, no mandatory cloud, no per-minute bill.</p><!-- /wp:paragraph --><!-- wp:columns --><div class="wp-block-columns"><!-- wp:column --><div class="wp-block-column"><!-- wp:heading {"level":3} --><h3 class="wp-block-heading">Record</h3><!-- /wp:heading --><!-- wp:paragraph --><p>One button captures audio to a memory card. Sync over Bluetooth, WiFi, or automatic upload when on charge.</p><!-- /wp:paragraph --></div><!-- /wp:column --><!-- wp:column --><div class="wp-block-column"><!-- wp:heading {"level":3} --><h3 class="wp-block-heading">Understand</h3><!-- /wp:heading --><!-- wp:paragraph --><p>Your server transcribes and summarises each recording, with the AI provider you chose: local, open, or commercial.</p><!-- /wp:paragraph --></div><!-- /wp:column --><!-- wp:column --><div class="wp-block-column"><!-- wp:heading {"level":3} --><h3 class="wp-block-heading">Own it</h3><!-- /wp:heading --><!-- wp:paragraph --><p>Audio, transcripts and summaries live on your storage. A completely open API means nothing is locked away.</p><!-- /wp:paragraph --></div><!-- /wp:column --></div><!-- /wp:columns --><!-- wp:separator --><hr class="wp-block-separator has-alpha-channel-opacity"/><!-- /wp:separator --><!-- wp:heading --><h2 class="wp-block-heading">Explore</h2><!-- /wp:heading --><!-- wp:list --><ul class="wp-block-list"><!-- wp:list-item --><li><a href="/how-it-works/">How it works</a> &mdash; the device, the sync paths, and the pipeline.</li><!-- /wp:list-item --><!-- wp:list-item --><li><a href="/bring-your-own-ai/">Bring your own AI</a> &mdash; connect any provider, open or commercial.</li><!-- /wp:list-item --><!-- wp:list-item --><li><a href="/open-source/">Open source</a> &mdash; the hardware, firmware, app and server.</li><!-- /wp:list-item --><!-- wp:list-item --><li><a href="/hosted-plans/">Hosted plans</a> &mdash; let us run the backend for you.</li><!-- /wp:list-item --></ul><!-- /wp:list --><!-- wp:paragraph {"fontSize":"small"} --><p class="has-small-font-size">Nightjar is an independent open-source project inspired by the Plaud class of device. It is not affiliated with, endorsed by, or derived from Plaud. Built by 12 Hobbies Studio.</p><!-- /wp:paragraph -->

View file

@ -0,0 +1,9 @@
<!-- wp:heading {"level":1} --><h1 class="wp-block-heading">Hosted plans</h1><!-- /wp:heading --><!-- wp:paragraph {"fontSize":"large"} --><p class="has-large-font-size">Nightjar is free to self-host. If you would rather not run a server, let 12 Hobbies Studio run the backend for you &mdash; with a plan to match how private you want it.</p><!-- /wp:paragraph -->
<!-- wp:paragraph --><p>You always keep the open device and the open app. The difference between the cloud plans is <strong>where the AI runs</strong>. The <strong>Cloud</strong> plans use best-in-class commercial AI for speed, quality and value. The <strong>Private</strong> plan processes everything on our own servers in the EU with open models, so your audio never reaches a third-party AI at all.</p><!-- /wp:paragraph -->
<!-- wp:columns --><div class="wp-block-columns">
<!-- wp:column --><div class="wp-block-column"><!-- wp:heading {"level":3} --><h3 class="wp-block-heading">Self-host</h3><!-- /wp:heading --><!-- wp:paragraph --><p><strong>Free, forever</strong></p><!-- /wp:paragraph --><!-- wp:list --><ul class="wp-block-list"><!-- wp:list-item --><li>Run everything yourself</li><!-- /wp:list-item --><!-- wp:list-item --><li>Your hardware, your AI keys</li><!-- /wp:list-item --><!-- wp:list-item --><li>Full source and docs</li><!-- /wp:list-item --><!-- wp:list-item --><li>Community support</li><!-- /wp:list-item --></ul><!-- /wp:list --><!-- wp:buttons --><div class="wp-block-buttons"><!-- wp:button {"className":"is-style-outline","width":100} --><div class="wp-block-button is-style-outline has-custom-width wp-block-button__width-100"><a class="wp-block-button__link wp-element-button" href="/guides/">Get started</a></div><!-- /wp:button --></div><!-- /wp:buttons --></div><!-- /wp:column -->
<!-- wp:column --><div class="wp-block-column"><!-- wp:heading {"level":3} --><h3 class="wp-block-heading">Cloud Starter</h3><!-- /wp:heading --><!-- wp:paragraph --><p><strong>from 6 pounds / month</strong></p><!-- /wp:paragraph --><!-- wp:list --><ul class="wp-block-list"><!-- wp:list-item --><li>Managed transcription and summaries</li><!-- /wp:list-item --><!-- wp:list-item --><li>10 hours of audio per month</li><!-- /wp:list-item --><!-- wp:list-item --><li>10 GB secure storage</li><!-- /wp:list-item --><!-- wp:list-item --><li>Open API access, email support</li><!-- /wp:list-item --></ul><!-- /wp:list --><!-- wp:buttons --><div class="wp-block-buttons"><!-- wp:button {"width":100} --><div class="wp-block-button has-custom-width wp-block-button__width-100"><a class="wp-block-button__link wp-element-button" href="/contact/">Register interest</a></div><!-- /wp:button --></div><!-- /wp:buttons --></div><!-- /wp:column -->
<!-- wp:column --><div class="wp-block-column"><!-- wp:heading {"level":3} --><h3 class="wp-block-heading">Cloud Pro</h3><!-- /wp:heading --><!-- wp:paragraph --><p><strong>from 18 pounds / month</strong></p><!-- /wp:paragraph --><!-- wp:list --><ul class="wp-block-list"><!-- wp:list-item --><li>Everything in Starter</li><!-- /wp:list-item --><!-- wp:list-item --><li>40 hours of audio per month</li><!-- /wp:list-item --><!-- wp:list-item --><li>Priority processing, 100 GB storage</li><!-- /wp:list-item --><!-- wp:list-item --><li>Team sharing, priority support</li><!-- /wp:list-item --></ul><!-- /wp:list --><!-- wp:buttons --><div class="wp-block-buttons"><!-- wp:button {"width":100} --><div class="wp-block-button has-custom-width wp-block-button__width-100"><a class="wp-block-button__link wp-element-button" href="/contact/">Register interest</a></div><!-- /wp:button --></div><!-- /wp:buttons --></div><!-- /wp:column -->
<!-- wp:column --><div class="wp-block-column"><!-- wp:heading {"level":3} --><h3 class="wp-block-heading">Private</h3><!-- /wp:heading --><!-- wp:paragraph --><p><strong>from 39 pounds / month</strong></p><!-- /wp:paragraph --><!-- wp:list --><ul class="wp-block-list"><!-- wp:list-item --><li>Processed on our EU servers with open models</li><!-- /wp:list-item --><!-- wp:list-item --><li>Your audio never touches a third-party AI</li><!-- /wp:list-item --><!-- wp:list-item --><li>40 hours of audio per month, 100 GB</li><!-- /wp:list-item --><!-- wp:list-item --><li>Priority support, data-processing agreement</li><!-- /wp:list-item --></ul><!-- /wp:list --><!-- wp:buttons --><div class="wp-block-buttons"><!-- wp:button {"width":100} --><div class="wp-block-button has-custom-width wp-block-button__width-100"><a class="wp-block-button__link wp-element-button" href="/contact/">Register interest</a></div><!-- /wp:button --></div><!-- /wp:buttons --></div><!-- /wp:column -->
</div><!-- /wp:columns -->
<!-- wp:paragraph {"fontSize":"small"} --><p class="has-small-font-size">Plans and pricing shown are indicative while the hosted service is in preparation. Register your interest and we will confirm the details and early-access pricing. All hosted data is kept in the EU.</p><!-- /wp:paragraph -->

View file

@ -0,0 +1,74 @@
<!-- wp:heading {"level":1} --><h1 class="wp-block-heading">How Nightjar works</h1><!-- /wp:heading --><!-- wp:paragraph {"fontSize":"large"} --><p class="has-large-font-size">From a press of a button to a searchable, summarised transcript, every step runs on hardware and services you control. Here is the whole workflow, end to end.</p><!-- /wp:paragraph -->
<!-- wp:heading --><h2 class="wp-block-heading">The workflow at a glance</h2><!-- /wp:heading -->
<!-- wp:html -->
<figure style="margin:0 0 8px;overflow-x:auto">
<svg viewBox="0 0 980 210" width="100%" role="img" aria-label="Nightjar workflow: record, sync, transcribe, summarise, export" xmlns="http://www.w3.org/2000/svg" style="font-family:ui-sans-serif,system-ui,sans-serif;max-width:980px">
<defs><marker id="ar" markerWidth="9" markerHeight="9" refX="7" refY="3" orient="auto"><path d="M0,0 L7,3 L0,6 Z" fill="#a1a1aa"/></marker></defs>
<g>
<rect x="16" y="55" width="160" height="100" rx="12" fill="#fafafa" stroke="#e4e4e7"/>
<text x="96" y="92" text-anchor="middle" font-size="16" font-weight="700" fill="#18181b">1. Record</text>
<text x="96" y="115" text-anchor="middle" font-size="12" fill="#52525b">One button, to</text>
<text x="96" y="131" text-anchor="middle" font-size="12" fill="#52525b">microSD. No limit.</text>
<rect x="220" y="55" width="160" height="100" rx="12" fill="#fafafa" stroke="#e4e4e7"/>
<text x="300" y="92" text-anchor="middle" font-size="16" font-weight="700" fill="#18181b">2. Sync</text>
<text x="300" y="115" text-anchor="middle" font-size="12" fill="#52525b">BLE, WiFi, or</text>
<text x="300" y="131" text-anchor="middle" font-size="12" fill="#52525b">on-charge upload</text>
<rect x="424" y="55" width="160" height="100" rx="12" fill="#fff7ed" stroke="#fdba74"/>
<text x="504" y="92" text-anchor="middle" font-size="16" font-weight="700" fill="#9a3412">3. Transcribe</text>
<text x="504" y="115" text-anchor="middle" font-size="12" fill="#7c2d12">Your server +</text>
<text x="504" y="131" text-anchor="middle" font-size="12" fill="#7c2d12">your chosen AI</text>
<rect x="628" y="55" width="160" height="100" rx="12" fill="#fff7ed" stroke="#fdba74"/>
<text x="708" y="92" text-anchor="middle" font-size="16" font-weight="700" fill="#9a3412">4. Summarise</text>
<text x="708" y="115" text-anchor="middle" font-size="12" fill="#7c2d12">Overview, points,</text>
<text x="708" y="131" text-anchor="middle" font-size="12" fill="#7c2d12">action items</text>
<rect x="832" y="55" width="132" height="100" rx="12" fill="#fafafa" stroke="#e4e4e7"/>
<text x="898" y="92" text-anchor="middle" font-size="16" font-weight="700" fill="#18181b">5. Export</text>
<text x="898" y="115" text-anchor="middle" font-size="12" fill="#52525b">Audio, TXT, SRT,</text>
<text x="898" y="131" text-anchor="middle" font-size="12" fill="#52525b">VTT, MD, JSON</text>
<line x1="180" y1="105" x2="214" y2="105" stroke="#a1a1aa" stroke-width="2" marker-end="url(#ar)"/>
<line x1="384" y1="105" x2="418" y2="105" stroke="#a1a1aa" stroke-width="2" marker-end="url(#ar)"/>
<line x1="588" y1="105" x2="622" y2="105" stroke="#a1a1aa" stroke-width="2" marker-end="url(#ar)"/>
<line x1="792" y1="105" x2="826" y2="105" stroke="#a1a1aa" stroke-width="2" marker-end="url(#ar)"/>
</g>
</svg>
</figure>
<!-- /wp:html -->
<!-- wp:heading {"level":3} --><h3 class="wp-block-heading">1. Record</h3><!-- /wp:heading --><!-- wp:paragraph --><p>The device is a small ESP32-S3 board with a digital MEMS microphone and a microSD card. A single button starts and stops recording; a status light shows what it is doing. Audio is written straight to the card as it is captured, so there is no length limit and nothing needs a network connection just to start recording. Each recording is saved with a small metadata file (duration, sample rate, and a unique id) so the rest of the pipeline can track it.</p><!-- /wp:paragraph -->
<!-- wp:heading {"level":3} --><h3 class="wp-block-heading">2. Sync</h3><!-- /wp:heading --><!-- wp:paragraph --><p>Nightjar has three independent ways to move recordings off the device, so one of them always fits your situation:</p><!-- /wp:paragraph --><!-- wp:list --><ul class="wp-block-list"><!-- wp:list-item --><li><strong>Bluetooth Low Energy</strong> handles control and status from the phone app: start or stop a recording, check the battery, and provision WiFi. It is low-power and always available.</li><!-- /wp:list-item --><!-- wp:list-item --><li><strong>WiFi to the app</strong> is the fast lane for transferring the actual audio. When your phone and the device are on the same network, recordings copy across quickly.</li><!-- /wp:list-item --><!-- wp:list-item --><li><strong>Independent upload on charge</strong> is the one that sets Nightjar apart. When the device is plugged in, it joins your WiFi on its own and uploads new recordings straight to your storage, with no phone involved. Leave it on the charger overnight and everything is waiting for you in the morning.</li><!-- /wp:list-item --></ul><!-- /wp:list -->
<!-- wp:heading {"level":3} --><h3 class="wp-block-heading">3. Transcribe</h3><!-- /wp:heading --><!-- wp:paragraph --><p>Once a recording reaches your server, it is transcribed into accurate, timestamped text. The transcription engine is your choice: a fully local model for maximum privacy, or a commercial speech-to-text API for the highest accuracy. Nothing is hard-wired to a single vendor.</p><!-- /wp:paragraph -->
<!-- wp:heading {"level":3} --><h3 class="wp-block-heading">4. Summarise</h3><!-- /wp:heading --><!-- wp:paragraph --><p>The transcript is then passed to a language model, which writes a structured summary: a short overview, the key points, and any action items. Again, the model is yours to pick, from a local model running on your own machine to a frontier commercial API. See <a href="/bring-your-own-ai/">Bring your own AI</a> for the full list.</p><!-- /wp:paragraph -->
<!-- wp:heading {"level":3} --><h3 class="wp-block-heading">5. Export</h3><!-- /wp:heading --><!-- wp:paragraph --><p>Everything is available through a completely open REST API. Pull the original audio, the plain-text transcript, subtitle files (SRT or VTT), a Markdown summary, or the full record as JSON. Because the API is documented and open, you can wire Nightjar into whatever you already use.</p><!-- /wp:paragraph -->
<!-- wp:separator --><hr class="wp-block-separator has-alpha-channel-opacity"/><!-- /wp:separator -->
<!-- wp:heading --><h2 class="wp-block-heading">How the pieces fit together</h2><!-- /wp:heading --><!-- wp:paragraph --><p>Four parts, all under your control, connected by an open API and a shared data model.</p><!-- /wp:paragraph -->
<!-- wp:html -->
<figure style="margin:0 0 8px;overflow-x:auto">
<svg viewBox="0 0 980 380" width="100%" role="img" aria-label="Nightjar architecture diagram" xmlns="http://www.w3.org/2000/svg" style="font-family:ui-sans-serif,system-ui,sans-serif;max-width:980px">
<defs><marker id="a2" markerWidth="9" markerHeight="9" refX="7" refY="3" orient="auto"><path d="M0,0 L7,3 L0,6 Z" fill="#a1a1aa"/></marker></defs>
<rect x="20" y="150" width="160" height="90" rx="12" fill="#fafafa" stroke="#e4e4e7"/>
<text x="100" y="185" text-anchor="middle" font-size="15" font-weight="700" fill="#18181b">Nightjar device</text>
<text x="100" y="207" text-anchor="middle" font-size="12" fill="#52525b">records to microSD</text>
<rect x="360" y="110" width="260" height="170" rx="12" fill="#f5f5f4" stroke="#e7e5e4"/>
<text x="490" y="138" text-anchor="middle" font-size="15" font-weight="700" fill="#18181b">Your server</text>
<rect x="384" y="152" width="98" height="46" rx="8" fill="#fff7ed" stroke="#fdba74"/><text x="433" y="180" text-anchor="middle" font-size="12" fill="#9a3412">Transcribe</text>
<rect x="498" y="152" width="98" height="46" rx="8" fill="#fff7ed" stroke="#fdba74"/><text x="547" y="180" text-anchor="middle" font-size="12" fill="#9a3412">Summarise</text>
<rect x="384" y="212" width="212" height="46" rx="8" fill="#ecfeff" stroke="#67e8f9"/><text x="490" y="240" text-anchor="middle" font-size="12" fill="#155e75">Open REST API + exports</text>
<rect x="760" y="30" width="200" height="80" rx="12" fill="#fff7ed" stroke="#fdba74"/>
<text x="860" y="62" text-anchor="middle" font-size="14" font-weight="700" fill="#9a3412">AI provider</text>
<text x="860" y="83" text-anchor="middle" font-size="12" fill="#7c2d12">local or commercial</text>
<rect x="760" y="150" width="200" height="80" rx="12" fill="#fafafa" stroke="#e4e4e7"/>
<text x="860" y="182" text-anchor="middle" font-size="14" font-weight="700" fill="#18181b">Your storage</text>
<text x="860" y="203" text-anchor="middle" font-size="12" fill="#52525b">S3 / WebDAV / NAS</text>
<rect x="760" y="270" width="200" height="80" rx="12" fill="#fafafa" stroke="#e4e4e7"/>
<text x="860" y="302" text-anchor="middle" font-size="14" font-weight="700" fill="#18181b">Phone app</text>
<text x="860" y="323" text-anchor="middle" font-size="12" fill="#52525b">Android and iOS</text>
<line x1="182" y1="195" x2="356" y2="195" stroke="#a1a1aa" stroke-width="2" marker-end="url(#a2)"/>
<text x="265" y="186" text-anchor="middle" font-size="11" fill="#71717a">sync</text>
<line x1="620" y1="150" x2="758" y2="82" stroke="#a1a1aa" stroke-width="2" marker-end="url(#a2)"/>
<text x="690" y="108" text-anchor="middle" font-size="11" fill="#71717a">uses</text>
<line x1="620" y1="195" x2="758" y2="190" stroke="#a1a1aa" stroke-width="2" marker-end="url(#a2)"/>
<line x1="620" y1="240" x2="758" y2="305" stroke="#a1a1aa" stroke-width="2" marker-end="url(#a2)"/>
</svg>
</figure>
<!-- /wp:html -->
<!-- wp:list --><ul class="wp-block-list"><!-- wp:list-item --><li><strong>The device</strong> captures audio and syncs it three ways.</li><!-- /wp:list-item --><!-- wp:list-item --><li><strong>Your server</strong> ingests recordings, runs transcription and summaries, and serves the open API.</li><!-- /wp:list-item --><!-- wp:list-item --><li><strong>Your AI provider</strong> does the language work, chosen by you and swappable any time.</li><!-- /wp:list-item --><!-- wp:list-item --><li><strong>Your storage and app</strong> keep and present everything. Nothing lives on someone else's cloud unless you choose it.</li><!-- /wp:list-item --></ul><!-- /wp:list -->
<!-- wp:heading --><h2 class="wp-block-heading">Ready to go deeper?</h2><!-- /wp:heading --><!-- wp:paragraph --><p>The <a href="/guides/">guides</a> walk through connecting an AI provider, self-hosting the server, and building the device yourself.</p><!-- /wp:paragraph --><!-- wp:buttons --><div class="wp-block-buttons"><!-- wp:button --><div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="/guides/">Read the guides</a></div><!-- /wp:button --><!-- wp:button {"className":"is-style-outline"} --><div class="wp-block-button is-style-outline"><a class="wp-block-button__link wp-element-button" href="/hosted-plans/">Or let us host it</a></div><!-- /wp:button --></div><!-- /wp:buttons -->

View file

@ -0,0 +1 @@
<!-- wp:heading {"level":1} --><h1 class="wp-block-heading">Open source</h1><!-- /wp:heading --><!-- wp:paragraph {"fontSize":"large"} --><p class="has-large-font-size">Every part of Nightjar is open, from the circuit to the summary. Build it, improve it, keep it.</p><!-- /wp:paragraph --><!-- wp:heading --><h2 class="wp-block-heading">The ethos</h2><!-- /wp:heading --><!-- wp:paragraph --><p>The person who records the audio should own it. That principle runs through the whole project. Nothing depends on a service that can be taken away, and nothing traps your recordings in a format only one company can read.</p><!-- /wp:paragraph --><!-- wp:list --><ul class="wp-block-list"><!-- wp:list-item --><li><strong>Own your data.</strong> Audio and transcripts stay on your storage.</li><!-- /wp:list-item --><!-- wp:list-item --><li><strong>No lock-in.</strong> Open hardware, open firmware, open API.</li><!-- /wp:list-item --><!-- wp:list-item --><li><strong>Reproducible.</strong> Anyone can build the whole thing from the documentation alone.</li><!-- /wp:list-item --></ul><!-- /wp:list --><!-- wp:heading --><h2 class="wp-block-heading">What is open</h2><!-- /wp:heading --><!-- wp:list --><ul class="wp-block-list"><!-- wp:list-item --><li><strong>Firmware</strong> for the ESP32-S3 device: recording, storage, sync and the on-device API.</li><!-- /wp:list-item --><!-- wp:list-item --><li><strong>Server</strong> that ingests recordings and runs transcription and summaries.</li><!-- /wp:list-item --><!-- wp:list-item --><li><strong>Mobile app</strong> for Android and iOS.</li><!-- /wp:list-item --><!-- wp:list-item --><li><strong>Hardware design</strong> and a bill of materials you can order.</li><!-- /wp:list-item --><!-- wp:list-item --><li><strong>3D-printed case</strong>, parametric so it fits your exact build.</li><!-- /wp:list-item --></ul><!-- /wp:list --><!-- wp:paragraph --><p>The firmware and software are copyleft, the hardware is open-hardware licensed, and the documentation is share-alike. Improvements stay open for everyone.</p><!-- /wp:paragraph --><!-- wp:heading --><h2 class="wp-block-heading">Build one</h2><!-- /wp:heading --><!-- wp:paragraph --><p>Off-the-shelf parts, a printed case, and a server you run. An ESP32-S3 with memory, an I2S microphone, a microSD card, a battery and a charging circuit. The firmware builds with common tooling, the case prints in a couple of hours, and the server runs on anything from a home NAS to a small cloud box.</p><!-- /wp:paragraph --><!-- wp:heading --><h2 class="wp-block-heading">The code is opening soon</h2><!-- /wp:heading --><!-- wp:paragraph --><p>The public repository is being prepared for release. Register your interest and we will let you know the moment it opens, so you can build, fork and contribute from day one.</p><!-- /wp:paragraph --><!-- wp:buttons --><div class="wp-block-buttons"><!-- wp:button --><div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="/contact/">Notify me when it opens</a></div><!-- /wp:button --></div><!-- /wp:buttons -->

View file

@ -0,0 +1,17 @@
<!-- wp:heading {"level":1} --><h1 class="wp-block-heading">Self-host the server</h1><!-- /wp:heading --><!-- wp:paragraph {"fontSize":"large"} --><p class="has-large-font-size">The Nightjar server ingests recordings, transcribes them, writes summaries, and serves the open API. It runs happily on a home NAS, a mini-PC, or a small cloud box.</p><!-- /wp:paragraph -->
<!-- wp:heading --><h2 class="wp-block-heading">What you need</h2><!-- /wp:heading --><!-- wp:list --><ul class="wp-block-list"><!-- wp:list-item --><li>A machine running Linux (or Docker) with Python 3.11 or newer.</li><!-- /wp:list-item --><!-- wp:list-item --><li>A few GB of RAM. More if you run a large local transcription model.</li><!-- /wp:list-item --><!-- wp:list-item --><li>Somewhere to keep audio: a local disk, a NAS, or S3-compatible storage such as MinIO.</li><!-- /wp:list-item --><!-- wp:list-item --><li>Optional: a local AI runtime (Ollama) if you want everything offline.</li><!-- /wp:list-item --></ul><!-- /wp:list -->
<!-- wp:heading --><h2 class="wp-block-heading">Step 1: Get the server</h2><!-- /wp:heading --><!-- wp:paragraph --><p>Clone the repository and enter the server directory.</p><!-- /wp:paragraph --><!-- wp:code --><pre class="wp-block-code"><code>git clone &lt;nightjar-repo&gt;
cd nightjar/server</code></pre><!-- /wp:code --><!-- wp:paragraph --><p>The public code is being prepared for release. <a href="/contact/">Register interest</a> to be notified when the repository opens.</p><!-- /wp:paragraph -->
<!-- wp:heading --><h2 class="wp-block-heading">Step 2: Install</h2><!-- /wp:heading --><!-- wp:code --><pre class="wp-block-code"><code>python -m venv .venv
. .venv/bin/activate
pip install -r requirements.txt</code></pre><!-- /wp:code -->
<!-- wp:heading --><h2 class="wp-block-heading">Step 3: Configure</h2><!-- /wp:heading --><!-- wp:paragraph --><p>Copy the example config and edit it. Set your storage, and your AI providers (see <a href="/connect-your-ai/">Connect your AI</a>).</p><!-- /wp:paragraph --><!-- wp:code --><pre class="wp-block-code"><code>cp .env.example .env
# then edit .env:
NIGHTJAR_STORAGE_BACKEND=local # or s3 / webdav
NIGHTJAR_LOCAL_MEDIA_DIR=./media
NIGHTJAR_LLM_PROVIDER=ollama
NIGHTJAR_TRANSCRIPTION_PROVIDER=local_whisper</code></pre><!-- /wp:code -->
<!-- wp:heading --><h2 class="wp-block-heading">Step 4: Run it</h2><!-- /wp:heading --><!-- wp:code --><pre class="wp-block-code"><code>uvicorn app.main:app --host 0.0.0.0 --port 8000</code></pre><!-- /wp:code --><!-- wp:paragraph --><p>Open <code>http://your-server:8000/docs</code> to browse the live API, and <code>http://your-server:8000/health</code> to confirm which providers are wired up.</p><!-- /wp:paragraph -->
<!-- wp:heading --><h2 class="wp-block-heading">Step 5: Point the device at it</h2><!-- /wp:heading --><!-- wp:paragraph --><p>In the phone app, set the server URL to your machine, and set the device&#8217;s upload target to your storage. From then on, recordings flow in automatically: the device uploads, the server transcribes and summarises, and the results appear in the app and through the API.</p><!-- /wp:paragraph -->
<!-- wp:heading --><h2 class="wp-block-heading">Keeping it running</h2><!-- /wp:heading --><!-- wp:paragraph --><p>For anything beyond testing, run the server under a process manager (a systemd service or a Docker container) so it restarts on reboot, and put it behind a reverse proxy with HTTPS. A small always-on box is ideal; a laptop that sleeps is not.</p><!-- /wp:paragraph -->
<!-- wp:buttons --><div class="wp-block-buttons"><!-- wp:button --><div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="/build-the-device/">Next: build the device</a></div><!-- /wp:button --><!-- wp:button {"className":"is-style-outline"} --><div class="wp-block-button is-style-outline"><a class="wp-block-button__link wp-element-button" href="/hosted-plans/">Skip it, let us host</a></div><!-- /wp:button --></div><!-- /wp:buttons -->

79
site/tools/apiscp.php Normal file
View file

@ -0,0 +1,79 @@
<?php declare(strict_types=1);
// ApisCP SOAP helper for openscribe.12hobbies.studio provisioning.
// Reads the API key from a scratch file so the secret is never embedded here.
// Usage:
// php apiscp.php --functions [filter] list SOAP operations (optionally grep)
// php apiscp.php <module_function> [json-arg ...]
// JSON args are decoded (so '["openscribe","/var/www/openscribe"]'-style or bare values work).
$key = trim((string)file_get_contents('C:/temp/claudetemp/apiscp.key'));
if ($key === '') { fwrite(STDERR, "no api key\n"); exit(2); }
$endpoint = 'https://howler.qsplace.co.uk:2083';
// Use a locally-cached WSDL to avoid a second HTTPS fetch; fall back to remote.
$wsdl = is_file('C:/temp/claudetemp/apnscp.wsdl')
? 'C:/temp/claudetemp/apnscp.wsdl'
: $endpoint . '/apnscp.wsdl';
// The panel cert is valid, but this PHP build ships no CA bundle, so relax verify.
$ctx = stream_context_create(['ssl' => ['verify_peer' => false, 'verify_peer_name' => false]]);
$client = new SoapClient($wsdl, [
'connection_timeout' => 30,
'location' => $endpoint . '/soap?authkey=' . $key,
'uri' => 'urn:net.apnscp.soap',
'trace' => 1,
'exceptions' => true,
'cache_wsdl' => WSDL_CACHE_MEMORY,
'stream_context' => $ctx,
]);
$cmd = $argv[1] ?? '';
// Drive wp-cli with the command string read from a file (avoids all shell-quoting pain
// when passing large HTML content). Usage: --wpcli-file <cmdfile> <hostname> <path>
if ($cmd === '--wpcli-file') {
$command = rtrim((string)file_get_contents($argv[2]), "\r\n");
$host = $argv[3];
$path = $argv[4] ?? '';
try {
$res = $client->__soapCall('wordpress_cli', [$command, [], $host, $path]);
echo json_encode($res, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES), "\n";
exit(isset($res['success']) && !$res['success'] ? 1 : 0);
} catch (Throwable $e) {
fwrite(STDERR, 'ERROR: ' . $e->getMessage() . "\n");
exit(1);
}
}
if ($cmd === '--functions') {
$filter = strtolower($argv[2] ?? '');
foreach ($client->__getFunctions() as $fn) {
if ($filter === '' || strpos(strtolower($fn), $filter) !== false) {
echo $fn, "\n";
}
}
exit(0);
}
if ($cmd === '') { fwrite(STDERR, "usage: php apiscp.php <module_function> [json-arg ...]\n"); exit(2); }
$args = [];
foreach (array_slice($argv, 2) as $a) {
if (strncmp($a, '@@', 2) === 0) { // @@path -> file contents as a string arg
$args[] = (string)file_get_contents(substr($a, 2));
continue;
}
$d = json_decode($a, true);
$args[] = ($d === null && $a !== 'null') ? $a : $d;
}
try {
$res = $client->__soapCall($cmd, $args);
echo json_encode($res, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES), "\n";
} catch (Throwable $e) {
fwrite(STDERR, 'ERROR: ' . $e->getMessage() . "\n");
// surface the raw response for debugging (no secrets in it)
if (method_exists($client, '__getLastResponse')) {
fwrite(STDERR, "LAST RESPONSE: " . substr((string)$client->__getLastResponse(), 0, 600) . "\n");
}
exit(1);
}

View file

@ -3,6 +3,49 @@
> A dated, append only log of decisions and their rationale. Newest at the top. Never > A dated, append only log of decisions and their rationale. Newest at the top. Never
> rewrite past entries; if a decision is reversed, add a new entry that says so. > rewrite past entries; if a decision is reversed, add a new entry that says so.
## 2026-07-05 - Backend infra: self-host on the MS-02 cluster (provision later)
- **Decision:** The hosted service will run primarily on owned hardware - 3x Minisforum
MS-02-class mini-workstations, one with an RTX 3050 6GB - fronted by a Cloudflare Tunnel.
Node A (GPU) = AI worker (faster-whisper + local LLM), Node B = app/API/Postgres/Redis,
Node C = MinIO storage + overflow + HA. Cloud VMs (Hetzner) are the fallback/hybrid.
**Not provisioned yet**; stand up at first beta / paying users. Full plan:
`docs/infrastructure.md`.
- **Context:** User has the MS-02 cluster on hand and prefers self-host for small scale.
- **Rationale:** The 3050 handles whisper easily and a 7-8B summary model fits 6GB; owned
hardware is cheaper than cloud and strengthens the Private-tier privacy story (audio
never leaves our premises). Cloud fallback covers uptime-sensitive growth.
- **Consequences:** Need Cloudflare Tunnel, backups/UPS, and to mind home-hosting uptime;
6GB VRAM caps local LLMs at ~8B until a bigger GPU is added.
## 2026-07-05 - Commercialise via a managed "hosted" service + plan tiers
- **Decision:** 12 Hobbies Studio offers a managed backend so people can use Nightjar
without self-hosting. Plans: Self-host (free), Cloud Starter (~GBP 6), Cloud Pro
(~GBP 18) on commercial AI, and Private (~GBP 39) on self-hosted open models (audio never
touches a third-party AI). Marketing site live; billing/signup not built yet (lead-gen
only). See `docs/hosted-service.md`.
- **Context:** User asked for commercial options where they host the backend.
- **Rationale:** Open project + managed convenience; the two backends map to the two plan
families. Cloud tiers keep good margin (cheap per-minute AI); Private is the privacy
premium.
- **Consequences:** Need metering, billing (Stripe), provisioning, a DPA, real pricing, and
a `hello@12hobbies.studio` mailbox before launch.
## 2026-07-05 - Rebrand product to "Nightjar" (repo stays `openscribe` for now)
- **Decision:** The product/commercial brand is **Nightjar** (site:
nightjar.12hobbies.studio). The repository keeps the name `openscribe` until a full
codebase rename is decided.
- **Context:** Due-diligence found "OpenScribe" unusable for a commercial brand (taken
domains, heavy in-category GitHub collision, weak/uncleared trademark). See
`docs/naming.md`.
- **Rationale:** "Nightjar" is distinctive, low-collision, and an arbitrary (strong)
trademark for AI software. Rejected: Mockingjay (Lionsgate TM), Lyrebird/Fathom
(in-category), dictionary words (taken/crowded), Recita (common).
- **Consequences:** Website rebranded. A full project/repo rename (SPDX, firmware
namespaces, docs, Forgejo repo) is deferred. Formal trademark clearance still needed.
## 2026-07-03 - Pluggable AI providers (bring your own AI) ## 2026-07-03 - Pluggable AI providers (bring your own AI)
- **Decision:** Transcription and summarisation each select a provider via config. LLM: - **Decision:** Transcription and summarisation each select a provider via config. LLM:

View file

@ -53,6 +53,16 @@ device, browses the library, plays audio and shows transcripts and summaries.
- The device syncs three ways as specified (BLE, WiFi-to-app, independent WiFi upload). - The device syncs three ways as specified (BLE, WiFi-to-app, independent WiFi upload).
- The whole stack is self-hostable and licensed for open reuse. - The whole stack is self-hostable and licensed for open reuse.
## Brand and commercial (2026-07)
- **Product brand: Nightjar** (the repo is still named `openscribe`; rename deferred - see
`docs/naming.md`).
- **Marketing site:** live at https://nightjar.12hobbies.studio (WordPress on ApisCP;
content + tooling saved in `site/`).
- **Commercial model:** open project + a managed "hosted" backend with plan tiers
(`docs/hosted-service.md`); backend infra plan in `docs/infrastructure.md` (self-host on
the MS-02 cluster, provision later).
## Key facts ## Key facts
- Trunk branch: `main` - Trunk branch: `main`

View file

@ -7,6 +7,18 @@
- [ ] (nothing active - M3 is next) - [ ] (nothing active - M3 is next)
## Pending - brand + commercial (Nightjar)
- [ ] Decide whether to rename the whole project/repo OpenScribe -> Nightjar (SPDX headers,
firmware namespaces, docs, Forgejo repo). Website already rebranded. See docs/naming.md.
- [ ] Formal trademark clearance for "Nightjar" (USPTO / UK IPO / EUIPO / attorney).
- [ ] Create the `hello@12hobbies.studio` mailbox (site forms/CTAs point at it) or change it.
- [ ] Confirm real hosted pricing + quotas (current numbers are indicative).
- [ ] Hosted service build: metering, Stripe billing + entitlements, tenant provisioning,
job routing (commercial vs Private node), DPA + retention policy. See docs/hosted-service.md.
- [ ] Provision backend infra at first beta/paying users (MS-02 cluster + Cloudflare Tunnel,
or Hetzner fallback). See docs/infrastructure.md. Optional: draft Docker Compose + tunnel config.
## Pending (roughly in build order) ## Pending (roughly in build order)
- [ ] M3 Independent uploader: charge/VBUS detect -> powered mode, S3-compatible + - [ ] M3 Independent uploader: charge/VBUS detect -> powered mode, S3-compatible +