Run plain portal connect as a background daemon - #105
Draft
davegaeddert wants to merge 3 commits into
Draft
Conversation
The database mode is no longer defaulted, so it is always visible in the command itself. That lets an agent's permission classifier (or a human reading a permission prompt) tell a read-only session from a writable one without knowing the previous default. --writable is renamed to --read-write to pair with --read-only.
connect was a foreground process holding the tunnel and a file lock for its lifetime, so an agent had to background it and the session died whenever that shell was reaped. It now spawns connect --foreground as a detached process (spawn, not fork -- os.fork() crashes on macOS), waits for the Unix socket to appear, and returns. plain portal disconnect stops the daemon via a pid file. Also: - Move the socket to the system temp dir keyed by a project hash. Under .plain/ it exceeded the AF_UNIX path limit on deep checkouts. - Flush the portal code from start immediately so it appears when stdout is a pipe or file. - Fix the README, which still described the pre-0.2 daemon.
The daemon now writes its pid into the flock'd lock file instead of a separate pid file the parent wrote. Liveness comes from the lock, so disconnect never signals a stale or recycled pid. connect no longer probes the lock or clears the socket itself -- the daemon does both, and readiness is its own 'Session active' line in the log.
davegaeddert
marked this pull request as draft
August 31, 2026 02:42
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #104.
Summary
plain portal connect <code>now spawnsconnect --foregroundas a detached daemon (spawn, not fork —os.fork()crashes on macOS, which is why the original daemon was removed in 0.2), waits until the daemon reportsConnected to remote. Session active., and returns. It's an ordinary blocking command again; norun_in_backgroundneeded for the local side.plain portal disconnectis back. The daemon records its pid in the flock'd lock file, so liveness comes from the lock anddisconnectnever signals a stale or recycled pid..plain/portal/to the system temp dir keyed by a project hash — under.plain/a deep checkout path exceeded theAF_UNIXpath limit (OSError: AF_UNIX path too long), which brokeconnect/execentirely.plain portal startflushes the portal code immediately, so it appears when stdout is a pipe/file (how an agent backgroundsheroku run). Previously it sat in the block buffer.--foreground,disconnect,status,/tmp/plain-portal.sock) — and the skill no longer tells agents to backgroundconnect.Why
In practice an agent had to background
connect, and the session died whenever that shell was reaped ("the session dropped after the first exec"). Combined with the unflushed portal code, the skill rarely worked first try.Test plan
start --read-only→connectreturns withSession active.→execworks → duplicateconnectrefused →disconnectstops daemon, remote exits,execreports no session, seconddisconnectis a no-op./scripts/test plain-portal,./scripts/type-check plain-portalNot done here
The
plain-devSupervisoralready implements this lock/pidfile/spawn/stop protocol; the daemon here mirrors its pattern but doesn't share code becauseplain-portaldoesn't depend onplain-dev. PromotingSupervisorinto coreplainwould let both use one implementation.