Docs: public README, setup guide and architecture, generic deploy files, and the T3 Code notice
Gateway / check (push) Successful in 9s
Gateway / image (push) Successful in 20s

This commit is contained in:
gib committed 2026-11-02 13:51:19 -05:00
1 parent b08fd9bdb1
commit f8241e42bb
13 files changed
+345 -301

No files matched your search

+47 -41
View File
@@ -1,63 +1,69 @@
# WristCode
An Apple Watch client for [T3 Code](https://t3.codes). Browse threads across every T3 Code
environment you run, reply by dictation, start new threads, and get a notification when an agent
finishes. It works over the watch's own cellular connection, with no phone nearby.
An Apple Watch app for [T3 Code](https://t3.codes). It shows every T3 Code environment you run on
your wrist: which agents need you, what each one is doing right now, and what finished. You can
approve commands, answer questions, reply by dictation and start new threads. It works over the
watch's own cellular connection, with no phone nearby.
Early work in progress.
<p>
<img src="watch/screenshots/feed-activity.png" width="200" alt="Working threads with what each agent is doing">
<img src="watch/screenshots/thread-approval.png" width="200" alt="A command waiting for approval">
<img src="watch/screenshots/thread-earlier.png" width="200" alt="A finished thread with its messages">
</p>
## What it does
- A feed of every thread across your machines. Threads waiting on you come first, with Allow and
Deny right on the card. Working threads show the step the agent is on, such as the command it is
running or the file it is editing. Finished threads say how many files and lines they changed.
- Notifications when a thread needs you, finishes or fails. Approvals and single-choice questions
can be answered from the notification. You choose which kinds you get.
- Replies and new threads by dictation. A new thread can use any model the host offers, and can
start in a mode that asks before every action.
- Every thread, settled ones too, by host and project, and search across all of them.
- Subagent requests show up on the card of the thread that started them.
- Complications and a Smart Stack widget with the request that has waited longest, or what is
running now.
- A status screen with each host's connection and route, and a test notification.
## How it fits together
watchOS only allows WebSockets for audio streaming apps, and T3 Code clients talk to their server
over a WebSocket. So the watch talks HTTPS to a small gateway, and the gateway talks to each T3 Code
watchOS only lets audio apps use WebSockets, and T3 Code clients talk to their server over one. So
the watch talks HTTPS to a small gateway, and the gateway keeps a WebSocket open to each T3 Code
server.
```
Apple Watch --HTTPS--> gateway --WebSocket--> T3 Code server (one per environment)
Apple Watch --HTTPS--> gateway --WebSocket--> T3 Code server (one per machine)
|
+--APNs--> notifications on the watch
```
- `gateway/` is the gateway: Bun and TypeScript, shipped as a container image.
- `watch/` is the watchOS app: SwiftUI, watch only, no iPhone companion.
- `docs/` holds the design notes and the T3 Code protocol reference.
- `mocks/` holds static UI mocks of the watch screens.
- `gateway/` is the gateway, in Bun and TypeScript, shipped as a container image.
- `watch/` is the watchOS app in SwiftUI. It is watch only, with no iPhone app.
- `deploy/` has a compose file and an example `.env` for the gateway.
- `scripts/renew-hosts.sh` renews the gateway's T3 sessions.
- `docs/` has the [setup guide](docs/setup.md), the [architecture](docs/architecture.md), the
[gateway API](docs/api.md) and the [T3 Code protocol notes](docs/t3-protocol.md).
- `mocks/` has the static HTML mocks I drew before building each screen.
## Running the gateway
## Setting it up
The gateway is one container with one SQLite file in `/data`. It runs under rootless podman on
junior.gib, the VPS. [`deploy/compose.yml`](deploy/compose.yml) is a copy of that stack, and
[`gateway/Containerfile`](gateway/Containerfile) builds the image. CI builds and deploys every push
to `main` that touches `gateway/`.
You need:
Operators use the `wristcode` CLI inside the container, on junior.gib:
- One or more machines running T3 Code.
- A machine to run the gateway container, reachable from the internet over HTTPS so the watch can
use it on cellular. It needs to reach each T3 Code server, over your LAN, a VPN or a public URL.
- An Apple developer account, to build the watch app and to send notifications.
```sh
# Connect a T3 Code server. Make the one-time credential on that server first:
t3 auth pairing create --scope orchestration:read --scope orchestration:operate --label "WristCode gateway"
podman exec -it wristcode-gateway wristcode host add https://t3mac.gbrown.org <credential> --label "Mac mini"
[docs/setup.md](docs/setup.md) walks through running the gateway, connecting your T3 Code
machines, building the app and pairing your watch.
# A host reachable more than one way lists fallbacks, preferred first. The gateway uses the
# first route that answers: here a direct WireGuard address, then the public URL.
podman exec -it wristcode-gateway wristcode host add http://desktop.gib:3773 <credential> --label "Desktop" --alt-url https://t3desktop.gbrown.org
## Status
# Pair a watch: enter the printed code on the watch within 5 minutes.
podman exec -it wristcode-gateway wristcode pair
podman exec -it wristcode-gateway wristcode host list
podman exec -it wristcode-gateway wristcode device list
podman exec -it wristcode-gateway wristcode device revoke <id or name>
```
The gateway exchanges the credential for a 30 day T3 session limited to reading and driving
threads, so the long-lived token never leaves the gateway. T3 sessions cannot be refreshed. A week
before one expires, the watch gets a notification. [`scripts/renew-hosts.sh`](scripts/renew-hosts.sh)
renews every host in one go over SSH, passing each one-time credential straight to the gateway
without printing it. Edit its host list to match your machines.
For local development, run `bun --watch src/main.ts` in `gateway/` with `WRISTCODE_DB` pointing at
a scratch file. Without APNs settings, pushes are logged instead of sent.
I use it every day with five machines. It is young, and the gateway API can change between
versions, so update the gateway and the app together.
## License
MIT
MIT. See [LICENSE](LICENSE). `gateway/src/awareness.ts` is ported from T3 Code, which is also MIT
licensed. See [NOTICE](NOTICE).