Service host initialization¶
This page describes the service host initializer and its environment snapshot.
Service host¶
Service initialization installs a per-user systemd unit on Linux or a per-user
LaunchAgent on macOS, plus a mode-0600 environment snapshot containing only the
allowlisted variables needed by the host. The preview is the default:
sase service init
sase service init --check
sase service init --diff
sase service init --yes
--check and --diff never write. Apply retires recognized legacy service units and
reloads the platform manager. Linux reports when user lingering is disabled because the
unit then stops with the login session. A non-default SASE_HOME changes the native
unit identity and therefore requires --force; the command prints the resulting
home-scoped identity before applying it. Use sase service uninstall with the same
preview, check, diff, force, and confirmation model to remove the managed unit.
Currency compares only what the host depends on. SSH_AUTH_SOCK and SSH_AGENT_PID are
ignored, and PATH is normalized before comparison: empty and duplicate entries are
stripped, trailing slashes are removed, and ephemeral sase_<N> workspace entries are
dropped. A real credential, root, or PATH change still rewrites the file.
SASE_TMPDIR and SASE_HOME are captured so the service host's managed_tmp_reap job
scans the same managed temp root that launched agents write to. A capture taken with a
different (or no) SASE_TMPDIR than the shell running the check shows up in
sase service init --check only as ordinary environment drift (a pending rewrite of the
captured environment file, exit 1); there is no dedicated temp-root warning. Rerun
sase service init --yes from a shell with the intended SASE_TMPDIR to realign them.
The reaper job itself also logs a warning when it scans a captured SASE_TMPDIR root
while the default $SASE_HOME/tmp root still holds entries; see
the housekeeping routine
for what that warning does and does not catch.
init --yes refuses a SASE agent process. When the calling shell sets SASE_AGENT or
SASE_AGENT_NAME, or its PATH contains an ephemeral workspace entry, apply exits 2
and names a login shell as the fix. Pass -a/--allow-agent-env (on both
sase service init and the sase init service alias) to capture deliberately.
--check and --diff stay available everywhere; only the capture-and-write path is
guarded.
The captured environment includes the SSH agent handle (SSH_AUTH_SOCK, plus
SSH_AGENT_PID when present) whenever it names a live socket. That is a convenience,
not a durable credential: an agent socket captured from a login shell
(/tmp/ssh-*/agent.*) belongs to that session and dies with it. At host start, a
captured agent socket that is missing is ignored and the manager's agent applies — on
Linux that is the systemd user manager's SSH_AUTH_SOCK, often a live but empty socket.
A captured login-shell socket is still not a durable credential. Do not count on it to
keep host-owned git work, such as plan archival from a Telegram approval, authenticated
across a reboot or a new login session.
Readiness is decided by the git remote, not by what an agent holds.
sase service init --check, sase service status, and plan approvals each ask the
remote to authenticate (ssh -o BatchMode=yes -T git@github.com) using the environment
in question, and only a refusal warns: Permission denied (publickey), or a login that
GitHub greets as a deploy key (Hi <owner>/<repo>!), which it scopes to that one
repository so every other push fails with denied to deploy key. A host that
authenticates by IdentityFile is healthy even though its agent is empty or absent, and
a host that cannot reach the network is reported as unknown rather than as a credential
failure. sase service status (and sase service init --check for an installed unit)
reports on both the environment init is about to capture and the effective one: the
captured file overlaid on what the manager hands the unit. A working credential in your
own shell cannot mask a refused one in the service.
Durability is a separate warning. When the checked environment authenticates through a
login-session agent but the same environment without that agent is refused,
sase service init, --check, sase service status, and the doctor check warn that
the service host's only accepted credential is that login-session agent. After a reboot
or logout the host falls back to the manager's agent (or no agent), which the git remote
refuses. The warning names the login-session socket and the fallback, points here for an
unattended credential, and reports captured and effective scopes separately. It stays
silent when either probe is unknown, when the agent already is the manager's, and when
an IdentityFile lets the fallback authenticate.
A plan approval that archives the plan (the commit option) runs the same check before
the decision is accepted. If the remote refuses the credential, the answer is refused
with the remediation and the gate stays pending, instead of being accepted and then
failing at terminal_prepare. An unknown answer never blocks an approval.
While the credential is refused, host chops that write beads cannot fetch their
operational workspace, so they fail with Permission denied (publickey). The external
issue mirror treats this as a credential failure: it reports auth_error, logs the git
error, and backs off exponentially instead of failing on every scheduled tick. Beads are
not mirrored until the credential is restored. Operational lease preparation retries a
transient transport failure (connection reset, timeout, DNS) up to three attempts. It
re-checks a Permission denied (publickey) once after about 10 seconds before failing,
so a brief remote-side refusal, such as during a GitHub incident, can recover before the
leased operation fails. A refusal that survives the re-check is reported with the
existing remediation.
The code cannot restore the credential, so this is yours to fix. There are two supported ways to give the host an unattended credential:
- Dedicated passphrase-less key via
IdentityFile(preferred). Generate an ed25519 key used only by the service host, add its public key to the GitHub account, and point theHost github.comblock of~/.ssh/configat it, keepingIdentitiesOnly yes(without it ssh offers every agent key before the file, and when one is a deploy key for another repository GitHub accepts it at login and then refuses every other repository withPermission to <repo> denied to deploy key):
ssh-keygen -t ed25519 -N "" -C "sase-service@$(hostname)" -f ~/.ssh/id_sase_service
IdentityFile ~/.ssh/id_sase_service
IdentitiesOnly yes
It is fully unattended, survives reboots and logout, and needs no agent. Verify it
without any agent:
env -u SSH_AUTH_SOCK -u SSH_AGENT_PID ssh -o BatchMode=yes -T git@github.com. Then
run ssh -T git@github.com from a login shell whose agent holds your other keys: it
must greet your account (Hi <login>!), not a repository (Hi <owner>/<repo>!). A
key protected by a passphrase cannot serve here, because nothing can type the
passphrase.
When ~/.ssh/config is shared across machines, guard the identity so machines
without the key are unaffected. Insert this directly after the existing
Host github.com block instead of editing that block:
# SASE service host unattended GitHub credential (sase docs/reference/agents/service-host.md).
# No-op on machines without the key.
Match originalhost github.com exec "test -r %d/.ssh/id_sase_service"
IdentityFile ~/.ssh/id_sase_service
IdentitiesOnly yes
- Load a key into the systemd user agent (interim). Run
SSH_AUTH_SOCK=<the reported socket> ssh-add <your private key>, using the socket the service host inherits rather than your shell's. It survives logout when lingering is enabled, but not a reboot, and needs your passphrase once per boot. After a reboot load the key into the manager's agent again withssh-add; a missing capture lets the manager's agent handle authentication.
After either, run sase service init and sase service restart, and confirm
sase service status reports no SSH warning.
sase service init also warns when an enabled command: entry in service.procs names
an executable that the host would fail to spawn. A bare first element is looked up on
the captured PATH, then next to the interpreter running SASE; the warning appears only
when both miss, and names the executable and the directory searched. The usual cause is
a plugin's console script under uv tool install --with, which does not link plugin
scripts onto PATH. Install the plugin into the same environment as sase, or give the
entry an absolute path. Without the warning the host would only report the failure after
repeated spawn failures, as a crash_loop proc in sase service status plus a
service notification (or, under restart: never, as a parked exited proc).
Bare sase init offers this machine-scoped initializer at most once per batch. A
decline is remembered for future broad onboarding runs on that machine, but it does not
disable the service feature or suppress an explicit sase service init.
Related¶
- Agents and workspaces — the section hub.
sase service¶
Bare sase service defaults to status, and bare sase service proc defaults to
proc list.
Moved to sase service.
Moved to Service host initialization.
Command behavior¶
Without --yes, init and uninstall only print the plan and the apply command. Their
--check forms are read-only and return 1 for drift. --force allows a non-default
SASE_HOME and uses a home-scoped unit identity; uninstalling that unit likewise
requires --force. Platform lifecycle installation supports Linux systemd user units
and macOS LaunchAgents. See
service configuration for entry fields and
layering.
sase service¶
The service host supervises machine-level daemon procs independently of the TUI that
started them. Bare sase service is read-only and delegates to sase service status;
bare sase service proc delegates to proc list.
sase service status
sase service start
sase service proc list
sase service proc restart scheduler
sase service proc run -- just check
start, stop, and restart control the host; run intentionally owns the foreground
terminal. logs -n N prints the bounded host log. status -j emits the host and every
configured proc as JSON and exits 0 while the host is running or starting, otherwise
1. When a native unit is installed, lifecycle commands use that platform manager;
otherwise start uses the detached host path.
sase service status prints the host summary (plus the host config error when one is
set) and a proc table with Restarts and Last exit columns; the proc State column
is colored by the same severity vocabulary as the Services tab (crash_loop and
backoff fail, exited fails while desired running (a clean give-up only warns),
unavailable warns, disabled and operator-stopped are muted). Below the table it
repeats the blockers and warnings sase service init would report, such as provider
CLIs missing from the captured PATH, a refused or login-session-only GitHub
credential, and service procs whose executable cannot be found.
When service.procs stops loading (for example a YAML error in a machine overlay), the
host keeps supervising its last-known-good configuration and reports the load error as
Host error: until the configuration loads again; running procs are not stopped because
the new configuration is unreadable. A proc the restart policy gives up on — a clean
exit under restart: on-failure, or any exit or spawn failure under restart: never —
is parked: the host stops relaunching it and it stays exited with its last exit until
sase service proc start NAME or restart NAME revives it, or its configuration
changes. A proc that enters a crash loop, or a desired-running proc that is parked,
raises a service notification naming the reason,
restart count, and log path; a parked proc's notification also names the reviving
sase service proc start NAME command.
sase service proc list carries the same Restarts and Last exit columns and
additionally exposes effective configuration, machine enablement, desired state, runtime
state, source, launcher, and log path. show NAME reports uptime, the restart count,
the last exit and when, the current restart decision reason, stop provenance, the
description, and any pending start/restart request. start NAME and restart NAME
record a durable request that the host consumes, then wait for the host to confirm it: a
restart prints service proc NAME restarted: pid OLD -> pid NEW, a start prints
service proc NAME started: pid N (or already running: pid N), and either exits
non-zero when the host cannot confirm, the proc is disabled or unavailable, or the host
is not running. A start or restart request also clears this boot's stop marker.
-n/--no-wait returns as soon as the request is recorded and -t/--timeout SECONDS
bounds the wait (default: the proc's stop timeout plus 10 seconds, at least 15).
stop NAME stops the proc until the next boot; enable NAME and disable NAME persist
a machine-local override. logs NAME prints that proc's bounded log. A configured proc
that is invalid remains visible as unavailable rather than preventing unrelated entries
from running.
sase service proc run -- COMMAND... submits a transient oneshot through the durable
proc service. It does not add the command to daemon desired state and the host never
replays it after restart. It runs outside the service host's process tree, records its
exit code, and appears in the Services tab's oneshots section under a #1–#9 index;
at most nine can be running at once, and finished ones never count. The TUI's !!
background commands use this same path. -c/--cwd, -l/--label, -p/--project, and
-w/--workspace add attribution; -j emits the created proc record.
Install or inspect the native user unit with sase service init. Without --yes it
prints a plan; --check is read-only and exits non-zero on drift, --diff includes the
unit and redacted captured-environment diff, and --yes applies. Linux uses a systemd
user service and macOS uses a LaunchAgent. A non-default SASE_HOME requires --force,
which creates a home-scoped unit identity. sase service uninstall has the same
plan/check/diff/apply shape; --force is also required when uninstalling the
non-default-home unit. See
service configuration and
initialization.
sase proc operates on durable procs: rows in ~/.sase/procs/procs.jsonl with combined
output logs under ~/.sase/procs/logs/. New sase proc run submissions always create
command rows under a supervisor; --session only controls attribution, and
--session none records an unattributed command visible in every session scope.
Historical detached rows remain readable and controllable. sase task is still
accepted as a deprecated alias.
The supervisor is independent of the submitting shell or TUI, so command work survives
TUI restarts and runs with no TUI open. The compact list markers are ⌘ for command,
▣ for tui, and ◆ for historical detached rows; sase proc show spells out the
kind and ownership. Use sase proc kill REF to stop any active store-backed proc. JSON
output uses a procs array for list and a proc object for run, show, and
kill. Retention keeps every pending or running proc plus the newest
procs.history_limit finished ones. See the
sase's TUI Procs tab for the full model and the
in-TUI equivalents.
sase proc run -N/--name NAME and sase proc list -N/--name NAME address a proc by a
named proc instead of its id: a bare name is derived beneath the calling sase-agent
(outside a sase agent a bare name is an error), while a fully qualified name
(<agent>--<name>; names may not contain /) is resolved exactly before falling back
to an exact proc id, then a unique id prefix. Active uniqueness is scoped per project,
and a name may be reused only after the proc holding it settles. sase proc show REF
and sase proc kill REF accept the same named proc, id, or id-prefix forms (an id
prefix needs at least three characters).
Every command group with an exact list child defaults to that list view when invoked
bare — sase agent, sase agent-cli, sase artifact, sase bead, sase chat,
sase completion, sase disk, sase file, sase file-history, sase file-hook,
sase final, sase flag, sase gate, sase goal, sase instructions,
sase machine, sase memory, sase migrate, sase monitor, sase notify,
sase plan, sase plugin, sase proc, sase project, sase prompt, sase repo,
sase skill, sase snippet, sase stitch, sase sudo, sase telemetry, sase tool,
sase usage, sase var, sase workspace, and sase macro. Nested groups such as
sase agent hold, sase agent prompts, sase agent tribe, sase artifact link,
sase artifact link relation, sase artifact trash, sase axe job,
sase axe routine, sase bead dep, sase bead ref, sase bead task-type,
sase memory web, sase patch ref, sase plan links, and sase project alias follow
the same rule. A bare invocation prints a short notice naming the delegation, for
example No subcommand provided for 'sase repo'; delegating to 'sase repo list'. Groups
without a list child, such as sase patch, sase axe, or sase bead pages, do not
delegate.
This is a property of the parser, not a hand-maintained list: any group that gains an
exact list child picks the behavior up automatically.
The bare form is only the default view. When you need flags that belong to the list
command, keep the list subcommand explicit, for example sase notify list -j,
sase agent hold list -j, or sase workspace list --json.
_[project]: A sase project is a named unit of work registered with SASE. _[workspace]: A sase workspace is a numbered clone of a project's primary repo, managed by the workspace store and tracked in that project's registry.json. _[agent]: A sase agent is an agent session or a single agent that does not belong to a session. _[prompt]: A prompt is the text SASE hands to a provider to start an agent turn. _[macro]: A macro is a reusable prompt triggered with #name in agent prompts. _[Patch]: A Patch is SASE's local unit of change. _[bead]: A bead is one tracked work item in SASE's git-native issue tracker. _[plan]: A plan is a persisted, approved artifact recording the intent behind agent work. _[TUI]: The TUI is SASE's terminal interface for running agents and reviewing results. _[epic]: An epic is an executable multi-phase plan run with sase bead work. _[gate]: A sase gate is a durable, command-backed request for one user decision, created when work must pause for the user. _[monitor]: A sase monitor is a monitor turn that runs one long command under a detached supervisor, so the command outlives the agent that started it. *[stitch]: A stitch is the lightweight ordered change record inside a Patch's STITCHES: section.