Context
Capture ideas away from the desktop: send a voice note, an image, or a link (e.g. an Instagram post URL) plus some accompanying thoughts from a phone, and have grandplan organize it into a note like any hotkey capture — media preserved as attachments, URLs preserved as link resources.
Transport decision: direct-to-intake — a phone shortcut / minimal PWA that POSTs to the existing HTTP intake (src/grandplan/adapters/http_intake.py) over a private network (Tailscale/WireGuard or LAN). No messenger platform in the loop: content never transits a third-party cloud, so the offline/zero-egress invariant holds end-to-end (QAS-1). The core app still never makes an outbound request.
Much of this exists already:
http_intake.py — token-authed (constant-time), 127.0.0.1-default intake built exactly for "POST content from a phone over LAN/VPN".
voice.py — offline STT via faster-whisper (grandplan[voice]); mic-based today, but the same local model transcribes an audio file.
- The organizer's
resources pipeline already extracts URLs present in the capture text into the note's ## Resources block (with the anti-hallucination containment check), so "thoughts + IG link" → link lands in the note with no new code.
Threads
- Attachments over intake — accept media alongside text (multipart or size-capped base64 field; current 1 MiB body cap must be rethought for voice notes/images, with an explicit new cap). Persist media into a vault attachments dir; reference from the note as
file resources. Reject unexpected content types; never execute/render untrusted content.
- Voice-note transcription — route received audio files through a file-based
Transcriber (reuse faster-whisper, local, offline). Transcript becomes the capture text (plus any typed thoughts); original audio kept as an attachment (lossless: the source artifact is preserved).
- Image + thoughts (v1) — store the image as an attachment resource; the accompanying text is the capture. (v2, deferred: optional local vision model to describe/extract — new multi-GB model on the 16 GB no-GPU target, needs its own evaluation.)
- Phone-side sender — an iOS/Android Shortcut recipe and/or a single-file PWA served by the intake (share-sheet friendly: share a post/photo/voice memo → adds thoughts → POST). Documented setup for Tailscale/WireGuard reachability.
Acceptance criteria
Non-goals (for this issue)
- Messenger-platform bridges (Telegram/WhatsApp bots). Considered and deferred: they route content through third-party clouds; if ever built, it would be a separate optional companion process, never core.
- Vision-model understanding of images (v2 candidate).
- Fetching remote content from shared URLs (violates zero-egress; the link itself is the artifact).
Related
Context
Capture ideas away from the desktop: send a voice note, an image, or a link (e.g. an Instagram post URL) plus some accompanying thoughts from a phone, and have grandplan organize it into a note like any hotkey capture — media preserved as attachments, URLs preserved as link resources.
Transport decision: direct-to-intake — a phone shortcut / minimal PWA that POSTs to the existing HTTP intake (
src/grandplan/adapters/http_intake.py) over a private network (Tailscale/WireGuard or LAN). No messenger platform in the loop: content never transits a third-party cloud, so the offline/zero-egress invariant holds end-to-end (QAS-1). The core app still never makes an outbound request.Much of this exists already:
http_intake.py— token-authed (constant-time), 127.0.0.1-default intake built exactly for "POST content from a phone over LAN/VPN".voice.py— offline STT viafaster-whisper(grandplan[voice]); mic-based today, but the same local model transcribes an audio file.resourcespipeline already extracts URLs present in the capture text into the note's## Resourcesblock (with the anti-hallucination containment check), so "thoughts + IG link" → link lands in the note with no new code.Threads
fileresources. Reject unexpected content types; never execute/render untrusted content.Transcriber(reusefaster-whisper, local, offline). Transcript becomes the capture text (plus any typed thoughts); original audio kept as an attachment (lossless: the source artifact is preserved).Acceptance criteria
## Resources(existing behavior verified through the new path).Non-goals (for this issue)
Related