Tooling · 5 min read
Prompting from anywhere with T3 Code
Until recently my coding agent lived inside VS Code, connected over SSH to my dev machine. That works well on one computer and falls apart on the second. The Claude Code session itself can be resumed from anywhere, but the VS Code window around it cannot. Switch machines and you are back to a terminal, reconstructing context. Checking on a long task from a phone was not on the table at all.
T3 Code changed that. It is a free, open source front end for coding agents that splits the work in two: a server that runs next to the code, and a client that can be a browser, a desktop app or a phone. The agent runs on the machine with the repo, and any client can pick a session up where the last one left it. It is not tied to one vendor either. I use it with Claude Code, but it drives Codex, OpenCode, Cursor and Grok Build as well, so switching agents is a setting rather than a new tool.
One machine that stays on
The only thing that runs all day is my dev machine. T3 Code runs on it as a systemd user service, and a timer restarts it every night so it picks up new releases without me having to think about it. My laptop can sleep, travel, or be off. The sessions keep going.
I did not want that server on the open internet. It sits behind
WireGuard, so reaching it from anywhere means bringing up the tunnel first and then opening the app. The phone and the laptop each have a peer; nothing else can see the port. That is the whole security model, and it is simple enough that I trust it.
Sessions like a mailbox
The part that makes this usable every day is the sidebar. Sessions are listed newest first, like an inbox, each under the project it belongs to. A project is just a repository with its own settings, and I have one per thing I work on, so the personal site and the work repos do not get mixed.
Starting a new session for a project takes one click from that list, and the sessions that are already running stay visible next to it. When a session finishes its work it settles into the list instead of demanding attention. I come back to it when I have time, read what happened, and either follow up or start the next one. I stopped waiting on long tasks and started checking on them.
Knowing what it costs
T3 Code does not resell tokens. You bring your own subscription, and the agent bills whatever it bills. What the app adds is a usage page that shows what that adds up to. For an agent that runs while I am not looking, that is the page I check most often.
Keeping it running
The quick start is npx t3@latest. That is fine in a terminal and wrong for a machine that has to stay up on its own. The process dies with the SSH session, does not come back after a reboot, and does not restart if it crashes. A systemd user service fixes all three and keeps the logs in journald. This is the setup on my machine.
The service itself:
~/.config/systemd/user/t3.service[Unit]
Description=t3 code
After=network.target
[Service]
ExecStart=%h/.local/share/fnm/aliases/default/bin/npx t3@latest --host 0.0.0.0
Restart=on-failure
RestartSec=5
Environment=PATH=%h/.local/share/fnm/aliases/default/bin:/usr/local/bin:/usr/bin:/bin
[Install]
WantedBy=default.target
Two details matter here. --host 0.0.0.0 makes the server listen on the WireGuard interface instead of loopback only. The full path to npx is there because my shell gets Node through
fnm, whose shell hook only runs in a login session, so the unit points at the stable default alias directly. If Node comes from a package manager, plain /usr/bin/npx does the job.
Because the unit runs npx t3@latest, every restart re-resolves latest. A daily restart is therefore also the auto-update. That takes a oneshot service and a timer in the same directory:
~/.config/systemd/user/t3-restart.service[Unit]
Description=Daily restart of t3 code (picks up latest t3 release)
[Service]
Type=oneshot
ExecStart=/usr/bin/systemctl --user try-restart t3.service
~/.config/systemd/user/t3-restart.timer[Unit]
Description=Restart t3 code daily at 04:30
[Timer]
OnCalendar=*-*-* 04:30:00
RandomizedDelaySec=10m
[Install]
WantedBy=timers.target
try-restart only touches the service when it is already running, so the timer never starts something that was stopped on purpose. Then enable everything:
systemctl --user daemon-reload
systemctl --user enable --now t3.service
systemctl --user enable --now t3-restart.timer
sudo loginctl enable-linger "$USER"
The last line is the one people miss. Without lingering, systemd kills a user's services when their last session ends and does not start them at boot, which is exactly the behaviour this setup is meant to avoid. With it, the server is up from the moment the machine is.
Day to day, three commands cover it:
systemctl --user status t3 # is it up
journalctl --user -u t3 -f # tail the logs
systemctl --user restart t3 # update now instead of waiting for 04:30
What changed
The agent used to be tied to a place: my desk, one editor window, one SSH connection. Now it is tied to a machine I never look at, and the place is wherever I happen to be with a phone and a tunnel. A prompt typed on the way home is running before I get there, and the result is waiting in the same list as everything else.