Before submitting
Area
apps/server
Steps to reproduce
- On macOS, run
t3 service install.
- Follow docs/user/source-control.md for Bitbucket: add
T3CODE_BITBUCKET_EMAIL and T3CODE_BITBUCKET_API_TOKEN to the service's environment. The only place launchd takes environment for this job is EnvironmentVariables in ~/Library/LaunchAgents/com.t3tools.t3code.service.plist, so add them there and launchctl bootout / bootstrap.
- Confirm Settings → Source Control shows Bitbucket authenticated.
- Run
t3 update (or run t3 service install again to "repair" the service, as docs/user/background-service.md suggests).
Expected behavior
Environment the user added to the service survives an update or reinstall, or there is a documented place to put service environment that t3 does not overwrite.
Actual behavior
The plist is re-rendered from a fixed template and the user's entries are gone. Bitbucket reverts to "Available. Set T3CODE_BITBUCKET_EMAIL and T3CODE_BITBUCKET_API_TOKEN on the server". The same happens to T3CODE_PORT / T3CODE_HOST pins, so a service pinned off the default port silently moves back to port scanning after an update.
Mechanics:
renderBootServicePlist (apps/server/src/cloud/bootService.ts ~L134) emits exactly PATH, T3CODE_HOME, and T3_BOOT_SERVICE_UNIT. renderBootServiceUnit does the same for systemd.
install() unconditionally runs writeDurably(unitPath, manager.render(plan)) (~L885). The only value read back from an existing unit is T3CODE_HOME via bootServiceBaseDirOf (~L64).
t3 update calls install() on every version switch (apps/server/src/cli/update.ts ~L559, comment: "The unit is rewritten either way").
- On Linux a
systemctl --user edit drop-in would survive this. launchd has no override mechanism, so on macOS there is nowhere to put service environment that t3 will not delete.
- docs/user/source-control.md tells Bitbucket users to export the variables "in the server's environment" and never mentions the background service; docs/user/background-service.md never mentions environment. Bitbucket is the only provider that needs environment, because it has no CLI to hold credentials.
Suggested fixes (any one would do):
- Preserve unknown
EnvironmentVariables / Environment= entries when re-rendering an existing unit.
- Have the launcher read an optional env file (e.g.
$T3CODE_HOME/service.env) and document it.
- Store Bitbucket credentials in the server's existing secrets store, settable from Settings → Source Control, so no environment is needed.
Impact
Blocks a workflow (Bitbucket source control cannot be kept working on a macOS background service across updates)
Version or commit
0.0.42 → 0.0.43-nightly.20260919.1962
Environment
macOS 26.6 (Darwin 25.6.0), arm64, background service via launchd
Logs or stack traces
# after `t3 update`, the regenerated plist EnvironmentVariables dict contains only:
PATH, T3CODE_HOME, T3_BOOT_SERVICE_UNIT
Workaround
Keep the variables out of t3's plist: a separate user LaunchAgent that runs launchctl setenv T3CODE_BITBUCKET_EMAIL … ; launchctl setenv T3CODE_BITBUCKET_API_TOKEN … ; launchctl kickstart -k gui/$UID/com.t3tools.t3code.service at login. The service inherits the domain environment and t3 never touches that file.
Before submitting
Area
apps/server
Steps to reproduce
t3 service install.T3CODE_BITBUCKET_EMAILandT3CODE_BITBUCKET_API_TOKENto the service's environment. The only place launchd takes environment for this job isEnvironmentVariablesin~/Library/LaunchAgents/com.t3tools.t3code.service.plist, so add them there andlaunchctl bootout/bootstrap.t3 update(or runt3 service installagain to "repair" the service, as docs/user/background-service.md suggests).Expected behavior
Environment the user added to the service survives an update or reinstall, or there is a documented place to put service environment that t3 does not overwrite.
Actual behavior
The plist is re-rendered from a fixed template and the user's entries are gone. Bitbucket reverts to "Available. Set T3CODE_BITBUCKET_EMAIL and T3CODE_BITBUCKET_API_TOKEN on the server". The same happens to
T3CODE_PORT/T3CODE_HOSTpins, so a service pinned off the default port silently moves back to port scanning after an update.Mechanics:
renderBootServicePlist(apps/server/src/cloud/bootService.ts~L134) emits exactly PATH,T3CODE_HOME, andT3_BOOT_SERVICE_UNIT.renderBootServiceUnitdoes the same for systemd.install()unconditionally runswriteDurably(unitPath, manager.render(plan))(~L885). The only value read back from an existing unit isT3CODE_HOMEviabootServiceBaseDirOf(~L64).t3 updatecallsinstall()on every version switch (apps/server/src/cli/update.ts~L559, comment: "The unit is rewritten either way").systemctl --user editdrop-in would survive this. launchd has no override mechanism, so on macOS there is nowhere to put service environment that t3 will not delete.Suggested fixes (any one would do):
EnvironmentVariables/Environment=entries when re-rendering an existing unit.$T3CODE_HOME/service.env) and document it.Impact
Blocks a workflow (Bitbucket source control cannot be kept working on a macOS background service across updates)
Version or commit
0.0.42 → 0.0.43-nightly.20260919.1962
Environment
macOS 26.6 (Darwin 25.6.0), arm64, background service via launchd
Logs or stack traces
# after `t3 update`, the regenerated plist EnvironmentVariables dict contains only: PATH, T3CODE_HOME, T3_BOOT_SERVICE_UNITWorkaround
Keep the variables out of t3's plist: a separate user LaunchAgent that runs
launchctl setenv T3CODE_BITBUCKET_EMAIL … ; launchctl setenv T3CODE_BITBUCKET_API_TOKEN … ; launchctl kickstart -k gui/$UID/com.t3tools.t3code.serviceat login. The service inherits the domain environment and t3 never touches that file.