# TaskForge Task management for game projects, written in Jai. The same client code runs as a native Windows app (OpenGL 3.3) and in the browser (WebGL2 via WASM). All UI and rendering is custom immediate-mode code; the only web code is a small JS shim that forwards GL calls, input, HTTP and media decoding. It runs in two modes: - **Local:** a single user, with the project in a text file (native) or the browser's storage (web). - **Server:** a team. `taskforge-server` (also Jai) stores users and the shared project in SQLite and serves the web client. People sign in from the browser or the native app, and changes sync between everyone. ## Features - **Custom fields** (Fields page): Text, Number, Checkbox, Select (one option), Tags (several options) and User (assign people). Options have colours, and a field can apply to tasks, epics or both. - **Views** for each context: - A **Board** turns any Select or Checkbox field into kanban columns, with drag & drop. - A **Checklist** turns each task into a checkbox. "Done" means a field has a chosen value, and items can be grouped by epic. - Views can filter by epic, by an option (e.g. `Discipline: Art`) or by a user (e.g. `Assignee: Me`), and choose which fields appear on cards. - **Epic rules** (Epic rules page): set a field on an epic from the state of its tasks, e.g. *Set **Art** to **Done** when **all** of its **Art** tasks have **Status** is **Done***. For each epic and field, the first matching rule from the top wins. If none match, the field keeps its value. - **Users** (server mode): sign-in with sessions, admins and deactivation. Assign people with User fields. Admins add users and reset passwords on the Users page; everyone can change their own password. - **Attachments:** images (PNG, JPEG, GIF, BMP, WebP) and videos (MP4/MOV, WebM, AVI) on tasks and comments. You can drop files onto the window, use the **Add image or video** button, or paste with Ctrl+V. The first attachment becomes the card's cover. Click a thumbnail to open the viewer; use Left/Right to move between attachments and Esc to close. - **Screenshots:** pasting a screenshot attaches it, e.g. after Win+Shift+S. The **Snip screen** button (native) opens the Windows snipping overlay and attaches the result automatically. **Capture screen** (web) grabs a frame of a screen, window or tab you pick. - **Video playback** in the viewer has play/pause (click or Space), seeking, time and mute. - Natively, Windows Media Foundation decodes video and audio inside the app: H.264/HEVC/AAC and anything else Windows has codecs for. Audio plays through waveOut and drives the clock. - In the browser, the browser's own decoder is used. - Attaching a video stores its size, duration and a poster frame, so thumbnails need no video decoding. - **Comments** on every task: - threads with replies, - editing that keeps the full edit history ("edited (2)" shows the earlier versions), - deletion that leaves a placeholder, so replies still make sense, - images, videos and screenshots inside comments. - **Text editing:** selection with the mouse (drag, double-click selects a word, triple-click selects all) and the keyboard (Shift+arrows/Home/End, Ctrl+A). Copy, cut and paste work on the selection, and typing replaces it. ## Building Requires the Jai compiler (tested with beta 0.2.030). ``` jai build.jai # native client: bin/TaskForge.exe (+ fonts.bin, libcurl.dll) jai build.jai - release # native client, optimized jai build.jai - web # web client: web/taskforge.wasm (add "release" for optimized) jai build.jai - server # server for this OS: server/bin/taskforge-server(.exe) jai build.jai - server-linux # Linux x64 server object file, linked on the droplet (see below) ``` Each client build bakes `data/Karla-Regular.ttf` (SIL Open Font License) into `fonts.bin` at compile time, so the app needs no font rasterizer at runtime. ## Running locally - **Native, local file:** `bin/TaskForge.exe [path/to/project.taskforge]`. - The project is a plain-text file that can live in your game's repository and diffs cleanly. - Attachments are stored next to it in `.taskforge.files/`. - **Web, local:** serve `web/` with any static server (`python -m http.server -d web 8000`). The project lives in the browser, with attachments in IndexedDB. Export/Import move the project (without attachments) to or from a file. - **Server on your machine:** ``` cd server/bin taskforge-server add-user daniel "Daniel" --admin # asks for a password taskforge-server --web ../../web --port 8080 # http://localhost:8080 ``` - `--demo` on the first start seeds the sample game project. Without it you get a clean starter project: Status, Assignee, Discipline and Priority fields, plus Board and My tasks views. - On Windows the server needs `sqlite3.dll` next to it; the build copies the one in `server/dev/`. - **Native app against a server:** `bin/TaskForge.exe --server https://tasks.example.com`. The session is remembered in `%APPDATA%\TaskForge\session.txt`. ## Deploying to a DigitalOcean droplet 1. Create an Ubuntu 24.04 droplet and point a domain's A record at its IP. The smallest size is fine for tasks; pick more disk if you'll attach a lot of video. 2. From Git Bash on your machine: ``` deploy/deploy.sh root@ tasks.example.com ``` The first run sets up the droplet (`deploy/setup-droplet.sh`): - installs SQLite, a C toolchain and Caddy, - creates a `taskforge` system user and a hardened systemd service, - configures HTTPS for your domain through Caddy (automatic Let's Encrypt), - enables the firewall, - adds a daily backup of the database and attachments to `/var/backups/taskforge` (database copies kept for 14 days). Every run builds the web client and the Linux server, uploads them, links the server on the droplet and restarts it. For later updates, run `deploy/deploy.sh root@`. 3. Create the first admin on the droplet: ``` sudo -u taskforge /opt/taskforge/bin/taskforge-server --db /var/lib/taskforge/taskforge.db add-user daniel "Daniel" --admin ``` Then open `https://tasks.example.com`, sign in, and add the rest of the team on the Users page. Why link on the droplet: the server is cross-compiled to a Linux object file on Windows, but linking a Linux executable needs Linux's system libraries, which the droplet has. Alternatively, copy your Jai distribution to the droplet and run `jai-linux build.jai - server` there. The backups live on the same droplet. For real safety, also enable DigitalOcean's droplet backups, or copy `/var/backups/taskforge` off the machine. Useful on the droplet: `journalctl -u taskforge -f` (logs), `taskforge-server --db ... list-users`, `taskforge-server --db ... set-password `. ## How the server works - **Database:** SQLite in WAL mode, loaded at runtime (`libsqlite3.so.0` / `sqlite3.dll`). Tables: `records` (the project), `users`, `sessions`, `login_failures`, `blobs`, `meta`. Each worker thread has its own connection; writes are short `BEGIN IMMEDIATE` transactions. - **Attachments** are content-addressed: - Files are named by their SHA-256 and stored in `blobs/` next to the database (`--blobs` moves it). - Uploads (`POST /api/blob`, up to 600 MB) stream to disk while being hashed, and only images and videos are accepted. - Downloads (`GET /api/blob/`) need a session. - **Project sync:** - A project is a set of records, one text line per entity (`task 42 ...`, `val 42 7 sel 5`, `comment ...`, `attach ...`), the same format as the local file. - Every write bumps a global revision. Clients send the records they changed (found by diffing against what the server last confirmed) and receive everything changed since their last revision. - The last writer wins per record, so two people editing different fields of the same task don't overwrite each other. - Clients poll every 2 seconds, and immediately after local edits. New ids come in blocks of 1000 per client, so they never collide. - **Security:** - Passwords are hashed with PBKDF2-HMAC-SHA256 (310,000 iterations, random salt). - Session tokens are 256-bit random values, stored only as SHA-256 hashes; they expire after 30 days of inactivity. - After 10 failed logins in 10 minutes, the username is locked for a while. - Changing a password signs out that user's other sessions. - Static file serving only accepts plain file names from the web directory. - HTTPS is handled by Caddy; the server listens on 127.0.0.1 only. - **HTTP API:** `GET /api/info`, `POST /api/login`, `/api/logout`, `/api/sync`, `/api/users`, `POST /api/blob`, `GET /api/blob/`. Current limits: - One project per server. - Changes arrive by polling (up to ~2 s delay) rather than push. - Deleting an attachment removes it from the project but leaves its file in the blob store. - Client size limits are 50 MB for images and 500 MB for videos. ## Code layout | File | Purpose | |---|---| | `build.jai` | Build metaprogram for all targets + font atlas baker | | `src/main.jai` | Client entry; picks the platform layer | | `src/platform_native.jai` | Win32 window, WGL, input, files, clipboard (incl. images), file picker, drag & drop, snipping, libcurl HTTP, `--sim` test harness | | `src/video_native.jai` | Media Foundation + waveOut video playback and probing (Windows) | | `src/platform_web.jai`, `web/taskforge.js` | WASM exports + the JS shim (WebGL2, input, fetch, localStorage/IndexedDB, image/video decoding) | | `src/web_gl.jai` | GL declarations for WASM (same names as the GL module) | | `src/render.jai`, `src/font.jai` | Batched renderer (shapes, text, images) and text | | `src/ui.jai` | Immediate-mode widgets: buttons, text editing with selection, dropdowns, scrolling | | `src/model.jai` | Fields, tasks, views, users, attachments, comments, epic rule evaluation | | `src/storage.jai` | Record format, local file, starter projects | | `src/sync.jai` | Sign-in and project sync with the server | | `src/media.jai` | Blob store, image cache, attaching files, video player state | | `src/attachments_ui.jai`, `src/comments.jai` | Thumbnails, viewer, card covers; comment threads | | `src/app.jai`, `src/account.jai` | Screens | | `src/sha256.jai` | SHA-256 / HMAC / PBKDF2 (shared with the server) | | `server/server.jai` | HTTP server, API, users, sessions, blob storage | | `server/sqlite.jai`, `server/crypto.jai` | SQLite binding; OS randomness | | `deploy/` | Droplet setup, deploy script, systemd unit, Caddyfile | ## Keyboard - `Esc`: close the viewer, a popup or the task panel - `Ctrl+S`: save / sync now - `Ctrl+V` with an image on the clipboard (task open): attach it - In the viewer: Left/Right for previous/next, `Space` for play/pause - In the comment box: `Ctrl+Enter` posts - In text fields: - `Shift`+arrows, `Home`, `End`: select - `Ctrl`+arrows: move by word - `Ctrl+A`: select all - `Ctrl+C` / `X` / `V`: copy, cut, paste - `Ctrl+Backspace`: delete a word - `Enter` submits a single-line field; `Ctrl+Enter` leaves a multi-line one ## Testing The native client can be scripted, for screenshots and regression checks: ``` bin/TaskForge.exe test.taskforge --sim "wait 2;click 710 230;wait 2;drop C:/art/concept.png;type Hello;shot out.bmp;quit" ``` Commands: `move X Y`, `down`, `up`, `click X Y`, `type TEXT`, `key CODE [MODS]`, `wheel LINES`, `drop PATH`, `wait N`, `shot FILE.bmp`, `quit`.