Homeport: a place for Codex to keep working

A private Python and Pydantic workspace for starting Codex tasks, leaving them running on a Mac mini, and returning to the same conversation.
On this page 5 sections
I wanted to start a coding task from my laptop, leave it running on my Mac mini, and check in later from either my laptop or phone. The repositories, processes, and conversation history should stay on the Mini. My laptop should be a way to reach the work, without having to stay open to keep it alive.
The first working version used SSH and tmux. I could connect from Ghostty, attach to Codex, disconnect, and return to the same terminal. That solved persistence, but I kept asking why I needed to attach to a terminal just to see how a task was going.
That question became Homeport: a small browser workspace around Codex, built with Python, Pydantic, FastAPI, and SQLite. The terminal remains available. The browser is the everyday interface.
Codex does the coding; Pydantic keeps the records
I use Pydantic at work, so it was a natural choice for validating Homeport's tasks, configuration, events, and results.
Codex already has the model loop, tools, command execution, and conversation handling. Homeport needs to validate task requests, remember which turn belongs to which task, record progress, and distinguish a completed result from a connection that disappeared. Those are useful jobs for Pydantic without introducing another model-driven orchestrator.
The task model includes a prompt, working directory, task ID, Codex thread and turn IDs, timestamps, timeout, state, and optional result. Pydantic validates those records when they enter or leave the application. SQLite stores them alongside structured events.
There is no Pydantic AI dependency in this version. If I eventually need an additional model to decide how to split or route work, that would be a separate design decision. For now, the worker has a straightforward job: send a validated task to Codex and keep track of what happened.
The Mini uses my normal ChatGPT login. We checked the app-server's account response and confirmed that it reported my Pro plan. No separate API key was added. That still means Codex's plan limits apply, and model inference still happens through OpenAI. The Mini hosts the agent processes, project files, and local state.
One server, more than one way into the conversation
Before selecting an interface, we inspected the installed CLI help and the official app-server documentation. The installed release was Codex CLI 0.160.0. It exposes an app-server, a queue command, and structured output from codex exec.
Homeport runs one persistent app-server with a private Unix socket. The Python worker communicates with it using JSON-RPC. It starts or resumes a thread, starts a turn, receives structured events, and reads the final turn state.
The interactive CLI can connect to that same server:
codex resume --remote unix:///path/to/codex.sock THREAD_ID
That detail mattered enough to test directly. We connected the TUI, submitted a task through the Python harness, and watched the prompt and progress appear in the terminal. A later follow-up remembered context from the preceding turn.
Starting a second app-server is not a way to attach to an existing terminal session. Both clients need to target the same live server. We also found that this release rejects permission override flags on remote resume: the CLI inherits the server's permissions instead.
The harness does not scrape terminal output or type prompts through tmux send-keys. tmux keeps the processes alive and provides an optional terminal to attach to; structured messages carry the actual work.
The browser is where I start and return
Homeport's first interface has a conversation list, a task composer, live progress, saved results, and a read-only file pane. The interface uses a black background and monospace type, closer to the terminal I already like working in. I can archive a session to clear the active list and restore it later; archiving keeps the history and does not stop running work. I can ask Codex to change something, inspect the files, and continue in the same conversation. On a narrow screen, it becomes one conversation at a time.
The path from a device to the work looks like this:
Laptop or phone browser
│ private HTTPS over Tailscale
▼
Homeport → SQLite queue → Python worker
│ Unix socket
▼
Codex app-server
▲
│
Optional Codex TUI
The web service binds to loopback on the Mini. Tailscale Serve provides a private HTTPS address, and Homeport checks the identity header supplied by Serve against the Mini owner's account. It also validates the request host and the origin of submissions. There is no public control endpoint or Codex token stored in the browser.
A real browser submission completed successfully. I sent a follow-up using the phone-sized layout, then reloaded the page and found both messages in the same conversation. The physical phone still needs its Tailscale sign-in refreshed, so I am distinguishing that responsive-browser test from a completed test on the phone itself.
A lost response is not permission to try again
The part I cared about most was what happens when the network fails after a task has been submitted.
Suppose Codex has already run a command, but the worker never receives the acknowledgement. Automatically submitting the prompt again might repeat a file edit or some external action. A retry that looks harmless at the HTTP layer can be a second instruction to an agent.
Homeport saves submission intent before requesting the turn. Once it has a turn ID, it can reconnect and observe that turn. If submission happened but acknowledgement is missing, the task becomes uncertain. It is not silently replayed, and later work on that thread waits for inspection.
The browser also generates and retains a request ID before sending. If its connection drops, checking that same submission uses the same ID. The database handles duplicate requests without adding a second task.
We tested recovery with a small command that waited 25 seconds and appended one line to a file. During the wait, the Python worker was terminated. Supervision restarted it, it recovered the acknowledged turn, and the file ended up with exactly one line.
We separately killed an actual SSH attachment and reconnected. The server, worker, and TUI kept their process IDs. Those are concrete tests of worker and connection recovery; they are not a claim that every failure mode is solved.
What keeps running, and what still needs work
A macOS LaunchAgent supervises the tmux panes for the server, worker, and web app. Closing a browser or losing the laptop's SSH connection leaves those processes on the Mini. After a reboot, the LaunchAgent starts when the macOS user logs in. Unattended boot and FileVault unlock are not configured or tested.
I deliberately configured Codex with full filesystem access and no command-approval prompts for this account. That is a consequential choice about what the agent can do on the Mini, separate from who can reach the interface. It still runs as an ordinary macOS user; it does not automatically get root or bypass privacy controls.
This is an early personal tool. The file pane previews code; it is not a full browser IDE. Tasks run through one worker. There are no push notifications or multi-user collaboration features. The app-server interface is experimental, so upgrading Codex deserves another round of testing.
The code is available in mager/homeport, including installation instructions, screenshots, and the verification notes. The current test suite has 19 passing tests around validation, persistence, uncertain outcomes, private web access, and file boundaries.
The useful change for me is that a coding conversation now has a home separate from the device I happen to be holding. I can open it, start something, leave, and return to the work.