Board

Tasks

Click a task title to expand or collapse its content.

To Do
rokojori-auth: end-to-end testing after session refresh overhaul The 7-phase auth session refresh overhaul (grace window, TokenUpdater, GuardedCall, Web Locks leader election, Electron token updater, new-session endpoint, heartbeat login) is deployed but not yet fully tested end-to-end. Verify: — Browser: multiple tabs open, let access token near-expire; confirm only one tab refreshes and others get state via BroadcastChannel. — Browser: confirm GuardedCall retries on transient network errors and blocks on expired. — Electron: let app run >1h, confirm token is refreshed proactively without restart. — Electron multi-instance: open two projects, confirm second instance skips login via heartbeat and mints an independent session. — rokojori-auth grace window: force a near-simultaneous double-refresh and confirm the second call resolves to the same replacement pair instead of 401ing. Rojo Chat: LLM Tools Allow users to define and use tools in rojo-chat-panel. Tools extend the LLM with callable functions (e.g. file read, web search, custom actions). The chat panel should support declaring a tool set, passing tool definitions to the model, handling tool-call responses, and feeding results back into the conversation. Rojo Chat: Claude API Integrate the Claude API (Anthropic) as a provider option in rojo-chat-panel. Allows testing and using Claude models alongside the existing OpenAI-compatible provider. Covers API key configuration, model selection, and verifying streaming and tool-use work end-to-end with the Anthropic SDK. Mobile: editor layout too tall, chat input not reachable On mobile the overall editor layout is still too tall — panels overflow the viewport and the chat input area is pushed out of view even after the min-height: 0 fix on rojo-chat-panel. Needs a broader mobile layout pass on the panel/section/editor structure. Code syntax highlighting in rojo-chat (Highlight.js) Code blocks in assistant responses are rendered as plain text inside pre/code tags. Integrate Highlight.js to apply syntax highlighting after markdown-it renders each response chunk. Apply highlighting to all code blocks in the assistant bubble. Rojo Character Editor Allow changing colors and selecting layers of a Rojo character from within the rojo-settings-panel. Colors map to fill/stroke/both targets on SVG elements; layers toggle visibility of named groups or swap between variants. The appearance field (colors[], layers[]) is already in the settings.rojo schema. Rojo Character Animation Box Animation system for emotional feedback during conversations. Animations can be scripted (predefined sequences) or driven dynamically by an LLM that emits emotion tags alongside its response. The animation box plays character animations (idle, happy, thinking, surprised, etc.) in the portrait area of the rojo-settings-panel and rojo-chat-panel. File tree: drag-and-drop move for files and directories Allow files and directories to be moved by dragging them within the file tree. Dragging a file onto a directory moves it inside; dragging a directory onto another directory moves the whole subtree. Use the existing file rename API (POST /api/files/:projectId/rename) — moving is a rename to a new parent path. Tab context menu on right-click (tabs and empty tab bar area) The tab ⋮ menu already works but should also open on right-click anywhere on the tab bar — both on individual tabs and on the empty space to the right of the tabs. Right-clicking a specific tab should also offer a "Close this tab" action directly. Unauthenticated Landing Screen Unauthenticated users currently crash on dashboard components. They should land on a screen that explains the app and shows a login link. Replace browser confirm() in group editor and account delete The group editor and account delete button still use the browser confirm() dialog. Replace with the custom <confirm-dialog> component already used elsewhere. MediaViewerPanel for images and PDFs Non-text files (images, PDFs) are visible in the file tree but not openable. Add a MediaViewerPanel and register it in FileEditorRegistry for common media types. Investigate Gitea webhook auto-deploy The webhook did not fire on the last two pushes to main. Check the Gitea webhook delivery log for the response code from /api/deploy. Also run journalctl -u roject -n 100 on the server to see whether the endpoint was reached at all. Most likely causes: signature mismatch, wrong branch ref, or the deploy command failing silently. Roject: wire lookup-email for member storage migration The rokojori-auth side (POST /api/auth/lookup-email) is live. Remaining Roject-side work: — Add SERVICE_SECRET to Roject .env (must match rokojori-auth SERVICE_SECRET) — In POST /api/projects/:id/members: call account.rokojori.com/api/auth/lookup-email, receive the user ID, store member_id as the user ID instead of the email — In source/server/projectAccess.ts change memberMatchesUser() from member.member_id === user.email to member.member_id === user.userId — Write a one-off migration script: for each member row, call lookup-email with the stored email and replace member_id with the returned user ID Switch Gitea webhook to dev branch The Gitea webhook currently triggers on pushes to main, redeploying on every commit. Switch to a dev branch so main is the stable release target. Ensure time is not depending on the user's clock JWT verification on the local server failed because the Windows client clock was ~65 minutes ahead of the production auth server clock. Any time-based logic that compares client-side time against server-issued timestamps (JWT exp, token TTL, session validity) is broken when clocks diverge. Work to do: — Audit all places where Date.now() / new Date() is used for security or session decisions; replace with server-authoritative time where possible. — On the auth side: use clockTolerance in jwt.verify as a configurable escape hatch (JWT_CLOCK_TOLERANCE env var, already added for local dev). — Write a developer guide covering: why user/client clock cannot be trusted, how to use server time for all authoritative checks, how to diagnose clock skew issues, and the JWT_CLOCK_TOLERANCE workaround for local dev. — Consider syncing advice in the local dev setup docs (future local-dev task).
In Progress
rokojori-tunnel — Phase 2 Production-deployed at tunnel.rokojori.com. Complete so far: relay server, CRUD API, streaming WebSocket protocol (res_start / res_data / res_end), HTTP proxy with SSE streaming, Electron Tunnel Agent (tray, login, tunnel list), Roject browse-tunnels button, Roject LLM chat via tunnel, client-side chunk animation. Remaining: — Allowed users list enforcement (multi-user private access) — Public access mode (no auth required on proxy route) — GET /api/tunnels/available with ?purpose= filter surfaced in Roject provider picker styles.rokojori.com — complete deployment Service is built and running manually via npm start on the server. Remaining work: — Set up systemd service (styles-rokojori.service) so it survives restarts — Download Barlow from /add-fonts (weights 100, 400, 700, 900) — Verify CORS is working for roject.rokojori.com font imports — Add to the add-subdomain deployment guide as a reference example
Done
Auth Update — session refresh overhaul Full plan and rationale: workspace/history/2026/08-August/02-Saturday/auth-update.page. Root cause was two bugs: browser tabs racing the same single-use refresh token (rokojori-auth), and Electron never refreshing after its one-shot startup call. — Phase 1: rokojori-auth refresh-token grace window. db.ts: RefreshToken gained usedAt/replacedBy; markUsed() marks-rotated instead of deleting; create() prunes expired rows. routes/auth.ts /api/auth/refresh: first use rotates + calls markUsed(); same token reused within 10s (REFRESH_GRACE_TTL) resolves to the same replacement pair instead of 401ing. — Phase 2: ActivityAnalyser gained OnVisibilityChange. New source/auth/TokenUpdater.ts: periodic 5-min timer + activity-triggered pings to GET /api/auth/me, EventSlot-driven valid | refreshing | expired | network-error state. Wired into editor-shell.ts. Browser access token is httpOnly, so proactive margin decision moved server-side: jwtMiddleware rotates within 15 min of real expiry. — Phase 3: source/auth/GuardedCall.ts: singleton wrapper with three tiers — user (no retry, throw), editor (3 retries 1s/3s/8s, throw), silent (same delays, never throws, console.warn). Pre-flight blocks on expired; waits up to 6 s on refreshing. Wired into Editor.ts and editor-shell.ts layout save/load. — Phase 4: Web Locks leader election in TokenUpdater.ts. First tab acquires roject-token-updater-leader, runs checks, broadcasts state via BroadcastChannel. Followers listen and mirror state. Leader handoff is automatic when the holder tab closes. — Phase 5: Electron token updater in electron/main.ts. Server clock offset from Date response header. JWT exp decoded directly (no httpOnly cookie). checkAndRefreshIfDue() every 5 min, refreshes within 15 min of expiry. — Phase 6: POST /api/auth/new-session in rokojori-auth/routes/auth.ts. requireAuth-guarded; mints a fresh independent token pair via issueTokenPair. — Phase 7: Session heartbeat in electron/main.ts. Running instance writes { accessToken, timestamp } every 10 s. New instance reads it on startup; if ≤30 s old calls POST /api/auth/new-session to skip login. Retired plaintext last-password.txt auto-login. Per-project per-device layout persistence Full tab tree (panels → sections → tab-containers → tabs including open files) saved to .roject/layout-<deviceId>.json inside each project directory. Remote projects: storage/<id>/root/.roject/. Local Electron: <localRoot>/.roject/. Remote proxy (Electron opening a roject.rokojori.com project): centralized layouts dir keyed by device + remoteProjectId. deviceId in localStorage already differentiates Firefox, Chrome, and Electron. Serialized format: { version, activePortraitPanel, panels: { left, center, right } } where each panel has sections[], each section has tabContainers[], each tab-container has tabs[] with { id, label, panelType, tag, openFile }. FileEditorPanel interface extended with getCurrentFile(): string | null, implemented in code-panel and page-editor-panel. Restored on editor load; falls back to default layout if none saved. makeResizeHandle() gained an optional onResize callback so inner handles trigger saves. .roject/ filtered from both remote (storage.ts) and local (localFiles.ts) file trees. Save triggers: panel resize, inner handle resize, tab click, file open, split, close-container, add-panel, portrait panel switch.