Charles Forsyth Projects
ultra-workstation

Local web workstation · v1.15.0

Ultra AI Workstation Desktop

Email, calendar, Slack and a work ledger in one window.

Read, triage, ask, draft with AI, revise, approve twice, send, then log and link the conversation in the ledger.

It runs on your own machine, listens on 127.0.0.1 by default, and keeps config and tokens outside the repo. Since v1.11 it can also hold a second, personal workspace built around your notes vault.

  • Python 3.12+
  • stdlib HTTP server
  • vanilla JS, no build step
  • MIT
CSS illustration of the calm layout, placeholder text only. Not a screenshot.

One stream, sorted by whose move it is

Email, ServiceNow ticket mail, Slack conversations and open ledger tasks land in one Desk stream, sorted by rules (not AI) into Mine, Waiting, Tasks and All. Every view has a key.

Desk

One stream of email, ticket mail, Slack and open ledger tasks. A context rail shows everyone on the conversation from the ledger.

Board o

My court, Waiting on (grouped by person, with Nudge), Watching (park an item until a date) and Done today.

Today g

Your calendar: drag an item onto a time to block it, meeting prep, find a time, RSVP. Ultra only changes blocks it created.

Day d

A morning check-in plan and an end-of-day report built from what you did. In a personal workspace both are built from your notes.

Ledger and Graph n v

Search people, labs, projects and tasks with reviewed writes. The Graph shows your ledger neighborhood and their links.

The bucket

Drag a conversation, person, lab or snippet into the bucket, then Log or Task (Task is hidden where ledger tasks are archive-only). Commit runs the ledger CLI and reads the record back.

Ask Hermes h

Ask your Hermes agent about what is on screen. It runs read-only; answers become a reply draft, log card, task card or bucket snippet.

Tidy the inbox

Previews a bulk archive with a reason on every row. It only removes the Inbox label, never deletes, and one Undo puts it all back.

Also included: research search and launch through the deep-research CLI (a new run only starts from the launcher after an estimate), read aloud in the browser voice, and optional AI audio summaries.

Work and Personal, kept apart

One Ultra can hold several workspaces, each with its own mail, calendar, ledger, history and sign-ins. Switch with the workspace button or W. A workspace with the ledger turned off never reads the ledger, so work tasks cannot show up in the personal one.

Life

Takes the Ledger's place in a workspace without a ledger: one tile per area of life, each with what is overdue, coming up and the next dated to-do.

Done in one click

Ticking a to-do updates the note with today's date as one git commit. If the note changed since the page loaded, it refuses and asks you to reload.

Log and Task to notes

Writes go to the vault through the same review card. Nothing is overwritten; each write is one git commit.

Archive-only ledger tasks (v1.15)

If your tasks have moved to another tracker, [ledger] new_tasks = false makes that workspace's ledger tasks archive-only: Task leaves the buttons, menus, palette and the t key, and the server refuses new ones. Old tasks can still be read, completed and re-dated. A personal workspace's Task (to the notes) is not affected.

House sensors (optional)

With a small sensor dashboard on your local network, Life tiles show readings. Read only, private addresses only.

One Python process, static page, JSON calls

The CLIs and APIs are the source of truth; the app is a view plus a staging area. Every write is a call to Gmail, Calendar, Slack (via Claude Code) or the ledger CLI, and every write is read back to confirm it landed.

Browser

Vanilla JS modules, no build step. Talks JSON over HTTP; every write carries a per-start CSRF token.

ultra server

  • stdlib ThreadingHTTPServer + guard
  • Desk, Board, Today, Day, Ledger tab
  • Draft Studio and double approval
  • Rules engine: court, tickets, noise (no AI)
  • SQLite state store with a write journal

Google

Gmail and Calendar APIs, one token per capability.

Local CLIs

nexus (ledger), claude (Slack), hermes, deep-research.

Optional

Gemini (on demand), notes vault MCP server, hosted MCP servers.

Long calls (Slack refresh, Ask, audio, ledger commits) run in threads and return a job id the page polls. Each optional tool is checked by ultra doctor; a missing one hides its feature and nothing else breaks. Demo mode registers the same routes over synthetic data and never touches a real account.

Nothing is sent without two clicks on two screens

  1. DraftAI output, including Ask Hermes answers and Board nudges, only ever becomes a new draft version.
  2. Approve (1 of 2)Locks the exact text. Any later edit sends it back to Draft.
  3. Review and send (2 of 2)Shows the final message, recipients and warnings.
  4. Short delaySends after a delay you can cancel. Slack posts are read back and compared.
  • Server-side checksThe server refuses to send if the approval token is unknown, used or expired, or if the stored message no longer matches what you approved.
  • Least privilege per actionFour separate Google tokens (read, modify, send, calendar), each granted or revoked on its own.
  • Archive, never deleteThe app has no trash, delete or mark-as-read action. Archive has Undo.
  • No background AIModels run only when a button is pressed. Model output can only fill cards; it has no path to an action.
  • Loopback by defaultListens on 127.0.0.1 only (port 7440). Remote mode admits only your local network and your own Tailscale devices; everything else is refused.
  • Untrusted content as textEmail HTML is converted to text, so no remote image or tracking pixel loads. Strict Content-Security-Policy.
  • Allow-listed ledger writesThe ledger adapter runs only allow-listed commands, passes entities as full UUIDs and sends text on stdin. Where ledger tasks are archive-only, every path refuses a new one.
  • Safe to publishNo credentials or personal data in the repo. CI runs a secrets scan; tests use synthetic fixtures only.

Install and run

Installs as a uv tool. Try it first with --demo: synthetic data, nothing real is read or sent.

Install and first run

uv tool install "git+https://github.com/charles-forsyth/ultra-workstation.git[google]"
ultra config init        # writes config.toml and style.toml (never overwrites)
ultra doctor             # checks setup without printing secrets
ultra start --demo       # synthetic data, nothing real is read or sent
ultra start              # your own accounts
ultra open

Pin a release with ...ultra-workstation.git@vX.Y.Z[google]. To upgrade, run the install again with --force, then ultra restart.

Google access: one token per capability

ultra auth google --capability read
ultra auth google --capability modify
ultra auth google --capability send
ultra auth google --capability calendar

Phone and other devices

ultra restart --host 0.0.0.0
ultra remote              # prints the addresses to open on the device

Optional tools

FeatureNeeds
AI summary, drafting, web search, audioGEMINI_API_KEY in the config folder's .env
Slack read and replyClaude Code with a Slack connector (claude on PATH)
Ledger, link chips, tasksthe nexus CLI (nexus serve makes it fast)
Notes vault (Home, Life)a local MCP server over your Obsidian vault
Ask Hermesthe hermes CLI
Researchthe deep-research CLI