Example: python -m http.server

First, a trivial example. Outer Shell only needs to know:

  • which systemd unit or launchd plist starts/stops the backend
  • which localhost port the app uses

This example serves files from your home directory on localhost:8000.

Linux

Create ~/.config/systemd/user/com.example.HttpServer.service:

[Unit]
Description=Example Python HTTP server
After=network.target

[Service]
Type=simple
ExecStart=/usr/bin/python3 -m http.server 8000 --directory %h
Restart=on-failure

[Install]
WantedBy=default.target

Enable the unit:

systemctl --user daemon-reload
systemctl --user enable com.example.HttpServer.service

Register it with Outer Shell:

outerctl backend upsert \
  --backend com.example.HttpServer \
  --name "HTTP Server" \
  --systemd-unit com.example.HttpServer.service

outerctl app upsert \
  --backend com.example.HttpServer \
  --name "HTTP Server" \
  --scheme http \
  --host 127.0.0.1 \
  --port 8000 \
  --path /

When the user opens the app, Outer Shell can start com.example.HttpServer.service, then connect to 127.0.0.1:8000.

Because these examples omit --frontend-id, outershelld derives the app’s frontend id as <backend>:main.

macOS

Create ~/Library/LaunchAgents/com.example.HttpServer.plist.

Replace /Users/example with your home directory:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>com.example.HttpServer</string>

  <key>ProgramArguments</key>
  <array>
    <string>/usr/bin/env</string>
    <string>python3</string>
    <string>-m</string>
    <string>http.server</string>
    <string>8000</string>
    <string>--directory</string>
    <string>/Users/example</string>
  </array>

  <key>WorkingDirectory</key>
  <string>/Users/example</string>

  <key>StandardOutPath</key>
  <string>/tmp/com.example.HttpServer.log</string>
  <key>StandardErrorPath</key>
  <string>/tmp/com.example.HttpServer.log</string>
</dict>
</plist>

Bootstrap it:

launchctl bootstrap "gui/$(id -u)" "$HOME/Library/LaunchAgents/com.example.HttpServer.plist"

Register it with Outer Shell:

outerctl backend upsert \
  --backend com.example.HttpServer \
  --name "HTTP Server" \
  --launchd-plist "$HOME/Library/LaunchAgents/com.example.HttpServer.plist"

outerctl app upsert \
  --backend com.example.HttpServer \
  --name "HTTP Server" \
  --scheme http \
  --host 127.0.0.1 \
  --port 8000 \
  --path /

This is intentionally boring. The app process owns the TCP listener, and Outer Shell just starts the service and opens the registered endpoint.