ink.rdco.dev — build spec
Provenance: inspired by MaximeRivest/riddle (MIT; reMarkable Tom Riddle diary — handwrite → page-PNG → vision LLM → reply animated as handwriting). Founder's insight chain (2026-07-21 iMessage, 14:33 + 14:47): port the concept to web/iPad → make it a PRODUCT: shared canvas, actor colors, knowledge grounding, configurable interaction — "make it work with our stack first, then turn it into a generalization for others."
Category claim: whiteboard apps have no brain; chat apps have no pen. ink is a pen-first thinking surface where an agent writes back — into the spot you point at.
Founder rulings (locked)
- New subdomain of rdco.dev → ink.rdco.dev (founder may rename).
- Secrets: v0 = per-user BYOK; the roadmap's option 4 = RDCO-held tokens + charge for access / usage markup (the productization path).
- Web first; iOS (PencilKit) kept in mind as a port target — keep the stroke model + interaction layer portable (no DOM-coupled data structures).
- Agent draws via constrained SVG (agreed limitation) — v1.
- Send-off = TRIPLE-CIRCLE an area. The gesture is also SPATIAL ADDRESSING: the circled region is where the agent responds. Flow: write instructions → triple-circle → circle + contents fade ("the page drinks the ink") → agent writes/draws its response into that region.
- Everything configurable: trigger (timed delay | gesture | button), vanishing vs persistent shared canvas, pen colors per actor (default: user black, agent blue).
v0 — tonight (private: founder + Andrew + Ray)
Stack: single static PWA (index.html + bundled assets) deployed via quick-sites → ink.rdco.dev behind Cloudflare Access (proven rails — no new infra, no deploy-classifier exposure). BYOK: each user pastes their own Anthropic API key once; stored ONLY in localStorage; calls go browser-direct to the Anthropic API (CORS-enabled direct browser access — the build agent MUST load the claude-api skill for exact headers/params/model ids rather than working from memory). Access grants: ben@ + ray@ at deploy; Andrew added via quick-share when founder supplies his email.
Canvas + pen:
- Pointer Events;
pointerType==='pen'draws (pressure → width, 1.5–4px); touch = two-finger pan/zoom only (palm rejection by type filtering); mouse draws too (desktop dev/testing). - Stroke model (portable, DOM-free):
{id, actor:'user'|'agent', color, baseWidth, points:[{x,y,p,t}]}. Document = ordered stroke list + viewport. Persist to localStorage (debounced) + Export/Import JSON buttons. Infinite-ish canvas (pan), paper-warm background, subtle grid off by default. - Eraser: toggle (or Pencil barrel-double-tap where the browser exposes it — do not depend on it).
Triple-circle detection:
- A candidate circle stroke: closed (‖start−end‖ < 15% of stroke bbox diagonal), roundish (bbox aspect between 0.5–2.0, pathLength / bboxPerimeter in [0.7, 1.6]), bbox diagonal > 60px.
- Trigger: 3 consecutive candidate circles within ~6s whose centroids lie within 30% of mean bbox diagonal of each other. Region = union bbox of the three, padded 12px.
- On trigger: the three circle strokes + strokes intersecting the region animate a fade-absorb (opacity + slight blur, ~900ms) and are removed from the visible doc (kept in history).
Send + reply:
- Render: full page context as one PNG (downscaled ≤1568px long edge) + the region crop as a second PNG. Send both to the model (region = "the request", page = "context") with a configurable system persona (default: a thoughtful diary that answers in the writer's language, concise, warm; no purple prose unless asked).
- Reply v0 = TEXT ONLY, streamed, then hand-written: bundle an OFL script font (Dancing Script) + opentype.js (bundled locally); lay the reply out to fit the target region (wrap, shrink-to-fit, overflow continues below the region); animate glyph outline paths stroke-by-stroke in the agent color; on completion the strokes become normal agent strokes in the document (persist mode) or fade after N seconds (vanish mode).
- Failure honesty: API errors surface as a small hand-written "the page is silent…" + a toast with the real error; never silent.
Settings panel (gear): trigger mode (triple-circle default | idle seconds | send button), persist vs vanish (+ vanish delay), user/agent colors, model picker, persona textarea, API key (with "stored only on this device" note), export/import, clear page.
PWA: manifest + icons + apple-touch meta so it installs to the iPad home screen; works offline for drawing (LLM calls need network, obviously).
v0 acceptance: on an iPad (or simulated pen events): draw with pressure → write "draw nothing, just say hi" → triple-circle it → ink drinks → blue handwriting answers into the circle → survives reload (persist mode) → second user's key works independently on their device → 0 console errors → design-critic pass.
v1 (next)
- Worker upgrade: per-user keys server-side, mapped from the Cloudflare Access JWT identity (same login, keys leave the browser). This is the same plumbing option-4 billing needs.
- Agent diagrams: constrained SVG grammar (paths/lines/ellipses/text only, viewBox = target region), validated then animated as pen strokes. Prompted alongside text.
- KB grounding: per-user corpus selection; founder's side can point at vault-derived indexes. BOUNDARY (standing): Andrew's client material → work-approved infra only, not rdco.dev.
- Page history + "show me what I wrote about…" recall (riddle's memory move, done with region-rise animation).
- Export page → vault note (close the knowledge loop).
v2 (productization)
- Live shared canvas (founder + Andrew + agent on one document) — Durable Objects; actor colors already in the model.
- Option-4 monetization: RDCO-held tokens, per-user metering, Stripe; Access → real auth for strangers.
- iOS native (PencilKit) reusing the stroke model + interaction spec.
Build discipline
- Implementation agent maintains
IMPLEMENTATION-NOTES-2026-07-21-ink-v0.mdin the app folder (decisions/deviations/tradeoffs/open questions) — required per the [[2026-05-18-implementation-notes-pattern-for-sub-agent-dispatches]] SOP. - App folder:
~/rdco-vault/01-projects/ink/app/(canonical source; quick-publish ships it verbatim — no secrets, no notes files in the folder... put IMPLEMENTATION-NOTES one level UP in01-projects/ink/). - Verify with Playwright: cache-busted loads, console clean, synthetic pen pointer events for draw + triple-circle, screenshot loop READ not just taken. Design-critic subagent pass before founder sees. Deploy = parent session via quick-publish.