You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs(autolaunch): document Linux backend (systemd + Flatpak portal) and update indices
- Add nucleus.autolaunch to Runtime Modules table and docs/runtime/index.md
- Document systemd user service and Flatpak Background portal backends on Linux
- Update wasStartedAtLogin detection signals (macOS: AppleEvent + LaunchInstanceID)
- Add complete Linux behavior section with unit generation and portal flow
- Update UNSUPPORTED states and openSystemSettings() notes
|`auto-launch`| Start the app at user login |`SMAppService` / `LaunchAgent`| Run registry / Startup folder |`.desktop` autostart |
131
131
|`secure-storage`| Hardware-backed secret storage for tokens, passwords, keys | Keychain | Credential Manager / DPAPI | Secret Service (`libsecret`) |
132
132
|`biometric-auth`| Prompt for fingerprint / face authentication |`LocalAuthentication` (Touch ID / Face ID) | Windows Hello |`fprintd` via D-Bus / polkit |
133
133
|`share-sheet`| OS share sheet (URL, file, text) |`NSSharingService`| Windows `DataTransferManager`| xdg-desktop-portal `Share`|
AutoLaunchState.UNSUPPORTED-> { /*macOS < 13, unsupported Linux env*/ }
41
42
}
42
43
```
43
44
@@ -65,7 +66,7 @@ Switch(
65
66
|`isUserLocked()`|`Boolean`|`true` when state is `DISABLED_BY_USER`|
66
67
|`enable()`|`AutoLaunchResult`| See rules below |
67
68
|`disable()`|`AutoLaunchResult`| — |
68
-
|`openSystemSettings()`|`Boolean`| Opens `ms-settings:startupapps` on Windows; `com.apple.LoginItems-Settings.extension` on macOS |
69
+
|`openSystemSettings()`|`Boolean`| Opens `ms-settings:startupapps` on Windows; `com.apple.LoginItems-Settings.extension` on macOS; no-op on Linux (no cross-DE "startup apps" URL)|
69
70
|`wasStartedAtLogin(args)`|`Boolean`|`true` if the process was launched by auto-launch. Works on both Win32 and MSIX |
70
71
71
72
### `AutoLaunchState`
@@ -77,7 +78,7 @@ Switch(
77
78
|`DISABLED_BY_USER`|**User toggled off via Task Manager / Settings — programmatic re-enable is blocked**|
78
79
|`DISABLED_BY_POLICY`| Blocked by Group Policy (MSIX only) |
79
80
|`ENABLED_BY_POLICY`| Forced on by Group Policy (MSIX only) |
80
-
|`UNSUPPORTED`| Platform or packaging not supported (Linux, sandboxed macOS PKG) |
81
+
|`UNSUPPORTED`| Platform or packaging not supported (macOS < 13, Linux without systemd / portal, missing JNI lib) |
81
82
82
83
### `AutoLaunchResult`
83
84
@@ -181,14 +182,43 @@ After `enable()`, macOS may leave the service in `requiresApproval` until the us
181
182
182
183
### Detection of an auto-launched start
183
184
184
-
`AutoLaunch.wasStartedAtLogin(args)` on macOS reads the `keyAELaunchedAsLogInItem` marker carried by the `kAEOpenApplication` AppleEvent that `loginwindow` dispatches at login. The detection is independent of the CLI `args` parameter — it is kept for API symmetry with Windows, where a marker argument is injected into the launch entry.
185
+
`AutoLaunch.wasStartedAtLogin(args)` on macOS combines two independent signals:
185
186
186
-
The native observer is installed at dylib-load time (`__attribute__((constructor))`), so it is in place before AWT's `NSApplication` starts its run loop and consumes the event. Call `AutoLaunch.wasStartedAtLogin(args)` anywhere in `main()` once the native library is loaded.
187
+
1. The `keyAELaunchedAsLogInItem` marker carried by the `kAEOpenApplication` AppleEvent that `loginwindow` dispatches at login. The native observer is installed at dylib-load time (`__attribute__((constructor))`), so it is in place before AWT's `NSApplication` starts its run loop and consumes the event.
188
+
2. The `LaunchInstanceID` environment variable that `launchd` injects into every process it spawns via `SMAppService`. Present at login-time start, absent on manual launches.
189
+
190
+
Either signal returning `true` is enough. The CLI `args` parameter is unused on macOS and kept for API symmetry with Windows.
187
191
188
192
### macOS < 13
189
193
190
194
`SMAppService` requires macOS 13.0+ (Ventura). On older releases, `AppServiceManager.isAvailable` returns `false` and every call reports `UNSUPPORTED` — there is no legacy fallback in the runtime.
191
195
196
+
## Linux behavior
197
+
198
+
Two backends, chosen at first access based on `ExecutableRuntime.isFlatpak()`. Both ride on a JNI bridge to GLib's GIO for D-Bus — no external library is linked at compile time.
199
+
200
+
### Host (deb / rpm / AppImage / dev runs) — systemd user service
201
+
202
+
`enable()` writes a transient unit at `~/.config/systemd/user/<app>.service`, calls `Reload` on `org.freedesktop.systemd1.Manager`, then `EnableUnitFiles` + `StartUnit`. `disable()` stops and disables the unit, then removes the file. `state()` is read from `ActiveState` / `UnitFileState` so the result reflects what systemd will actually do at next login — not just what is on disk.
203
+
204
+
The generated unit uses `Type=simple` with `WantedBy=default.target` and the process's own `ProcessHandle.current().info().command()` as `ExecStart` (override via `AutoLaunchConfig.executablePath`). The `--nucleus-autostart` marker is appended to `ExecStart` so `wasStartedAtLogin` works symmetrically with the other backends.
205
+
206
+
Login detection uses `INVOCATION_ID` — systemd injects it into every unit it spawns and it is not inherited across re-exec, so it is a reliable "started by systemd" signal even if the CLI marker is stripped.
207
+
208
+
### Flatpak — `org.freedesktop.portal.Background`
209
+
210
+
Inside a Flatpak sandbox, the user's systemd is unreachable — the portal is the only supported path. `enable()` calls `org.freedesktop.portal.Background.RequestBackground` with `autostart=true`, a `commandline` of `["flatpak", "run", "<app-id>", "--nucleus-autostart"]`, and the user-facing reason from `AutoLaunchConfig.backgroundReason` (default: `"Launch <appName> at login"`).
211
+
212
+
`state()` inspects the `~/.var/app/<id>/.../autostart/<id>.desktop` file exposed to the sandbox. `disable()` calls the same portal method with `autostart=false`. Both operations are asynchronous at the portal level; the bridge waits for the `Response` signal so calls are effectively synchronous from Kotlin.
213
+
214
+
### Dependencies
215
+
216
+
No extra module is required — the Linux native library ships inside `nucleus.autolaunch`. The backend uses `dlopen` on `libgio-2.0.so.0` at runtime, so the JAR runs unchanged on any modern Linux distribution (GLib 2.56+).
217
+
218
+
### `openSystemSettings()` on Linux
219
+
220
+
Returns `false`. There is no cross-desktop "startup apps" URL (GNOME, KDE, XFCE each manage autostart differently). Consumers that want to surface a shortcut can open `gnome-session-properties` or the distribution's equivalent themselves.
221
+
192
222
## Configuration overrides
193
223
194
224
All defaults fall back to `NucleusApp`. Override any of them **before** the first `AutoLaunch` call:
@@ -217,8 +247,10 @@ Detection is transparent across packaging types and uses the **signal that is de
217
247
| Backend | Mechanism |
218
248
|---|---|
219
249
| Win32 (MSI / NSIS) | Looks for the marker argument (`--nucleus-autostart` by default, configurable via `AutoLaunchConfig.autostartArgument`) written into the `HKCU\...\Run` command line |
220
-
| macOS user-dir LaunchAgent | Same marker argument, injected into the plist's `ProgramArguments`at `enable()` time|
250
+
| macOS `SMAppService.mainApp`| Two signals — (1) the `kAEOpenApplication` AppleEvent carrying `keyAELaunchedAsLogInItem`, intercepted at dylib-load time before AWT consumes it; (2) the `LaunchInstanceID` env var that `launchd` injects into SMAppService-spawned processes. Either one fires `true`|
221
251
| MSIX packaged desktop | Walks up the process tree (skipping self-spawned jpackage launcher chains) and checks if the external ancestor is `sihost.exe` — the Shell Infrastructure Host that launches MSIX startup-task activations. Manual launches (Start menu, Explorer, taskbar) come from `explorer.exe` or `runtimebroker.exe`|
252
+
| Linux systemd user unit | Reads `INVOCATION_ID` — systemd injects it into every unit it spawns. Present at login-time start, absent on manual launches from a terminal or `.desktop` entry |
253
+
| Linux Flatpak portal | Looks for the marker argument injected into the portal's `commandline` at `enable()` time. `flatpak run <app-id>` is a single token so the portal's historical `Exec=` quoting bug does not apply |
222
254
223
255
Both paths are fully deterministic — no heuristics, no timing guesses, no runtime dependencies beyond the shipped native DLL.
0 commit comments