|
1 | 1 | # launchd |
2 | | -Deterministic deploy orchestrator for single-binary Go services anchored to Linux systemd. launchd operationalizes a minimal, auditable deploy surface by composing battle-tested primitives: the resident Go toolchain for compilation, OpenSSH for transport and privileged control, systemd for service lifecycle, an optional migrator for schema evolution, and an HTTP health contract for liveness attestation. The objective function is predictability under duress, not novelty; the codebase is dependency-thin and tuned for reproducible behavior across heterogeneous fleet baselines. |
3 | | - |
4 | | -## Conceptual Premise |
5 | | - |
6 | | -The dominant substrate for “small-but-critical” services is a single daemon under systemd supervision. For these domains, heavyweight CI/CD machinery introduces variance without proportional utility. launchd prefers an austere convergence model: converge a binary onto a machine, converge a unit into systemd, converge the database state via idempotent migrations, and converge process readiness via health probes. Each convergence step is a transparent syscall to a canonical tool, yielding failure surfaces that are legible to any seasoned operator. |
7 | | - |
8 | | -Assumptions: |
9 | | -- A Go main package constitutes the service. |
10 | | -- SSH reachability with privilege elevation exists to manage units. |
11 | | -- The service exposes `/health` on a chosen TCP port. |
12 | | - |
13 | | -## MVP Scope |
14 | | - |
15 | | -- CLI: `launchd deploy --host <ip> --user <ssh-user> --app ./path/to/app --port <port> [--timeout <dur>]`. |
16 | | -- Pipeline: Compile ➝ Transfer ➝ systemd Provision ➝ Migrate (optional) ➝ Health Gate. |
17 | | -- Properties: idempotent (safe re-entrance), deterministic (explicit tooling), strict error surfacing, and minimal environmental preconditions (Go + OpenSSH). |
18 | | - |
19 | | -### Stage Semantics |
20 | | -- Compile: `go build -o /tmp/<app>` using the resident toolchain; no hermetic wrapper. |
21 | | -- Transfer: `scp` to `/usr/local/bin/<app>` after ensuring parent directory ownership is sane. |
22 | | -- systemd: materialize a unit, `daemon-reload`, `enable`, `restart`—idempotent operations. |
23 | | -- Migrations: opportunistic `migrate up` if a migrator exists on the target PATH. |
24 | | -- Health: poll `http://<host>:<port>/health` until success or deadline expiry. |
25 | | - |
26 | | -## Example Usage |
| 2 | +Launchd is a simple tool that helps you deploy a Go program (a single binary) to a Linux server that uses systemd. Think of it as a very small CI/CD helper you can run from your laptop. |
| 3 | + |
| 4 | +It does five things for you: |
| 5 | +- Build your Go app locally. |
| 6 | +- Copy the binary to your server over SSH. |
| 7 | +- Create/refresh a systemd service for it. |
| 8 | +- Optionally run database migrations. |
| 9 | +- Check that the app is healthy before finishing. |
| 10 | + |
| 11 | +If you’re new to servers, follow this guide step by step. You don’t need Docker or a complex CI system. |
| 12 | + |
| 13 | +## What you need (prerequisites) |
| 14 | +- A Go project with a `main` package you can build. |
| 15 | +- Your laptop/workstation with: |
| 16 | + - Go installed |
| 17 | + - OpenSSH client (`ssh`, `scp`) |
| 18 | +- A Linux server with: |
| 19 | + - systemd (most modern distros have it) |
| 20 | + - SSH access (you can log in with a user that can use `sudo`) |
| 21 | + - Network port open for your app (e.g., 8080) |
| 22 | +- Your app should expose a health endpoint like `GET /health` that returns 200 OK when ready. |
| 23 | + |
| 24 | +## Install / Build launchd |
| 25 | +Clone this repo and build the CLI: |
27 | 26 |
|
28 | 27 | ```bash |
29 | | -launchd deploy --host 203.0.113.10 --user ubuntu --app ./examples/hello --port 8080 --timeout 60s |
| 28 | +git clone <this-repo-url> |
| 29 | +cd launchd |
| 30 | +go build -o launchd ./cmd/launchd |
30 | 31 | ``` |
31 | 32 |
|
32 | | -On completion, the service is registered as `<app>.service`, executes `/usr/local/bin/<app> --port=<port>`, and is enabled for boot. |
| 33 | +This creates a `launchd` binary in the project root. Optionally, move it into your PATH: |
33 | 34 |
|
34 | | -## Design Guarantees |
| 35 | +```bash |
| 36 | +mv launchd /usr/local/bin/ |
| 37 | +``` |
35 | 38 |
|
36 | | -- Determinism: all side effects mediated by explicit tools (`go`, `scp`, `ssh`, `systemctl`). |
37 | | -- Idempotence: repeated invocations converge; non-destructive `enable`, guarded migrations. |
38 | | -- Failure Locality: stages short-circuit with precise logging; no ambiguous partial states. |
39 | | -- Observability: microsecond timestamps and stage banners designed for operator cognition. |
40 | | -- Minimality: no bespoke protocol layers; everything deferential to Unix contracts. |
| 39 | +## Prepare your app |
| 40 | +Make sure your app builds locally: |
41 | 41 |
|
42 | | -## Future Roadmap |
| 42 | +```bash |
| 43 | +go build -o ./bin/myapp ./path/to/your/cmd |
| 44 | +``` |
43 | 45 |
|
44 | | -- Artifact integrity: checksums, content-addressed remote layout, atomic swaps. |
45 | | -- Principle of Least Privilege: dedicated system users, hardening of unit sandboxing. |
46 | | -- Transport hardening: native SSH client (keyboard-interactive, agent-forwarding policies) while preserving zero-daemon requirements. |
47 | | -- Policy engines: JSON-structured logs, exponential backoff policies, retry budgets. |
48 | | -- Migration adapters: goose, golang-migrate, app-native hooks with transactional guards. |
| 46 | +Also ensure your app can start with a port flag or env (for example `--port 8080`) and serves `/health`. |
49 | 47 |
|
50 | | -## Organizational Context |
| 48 | +## Quick start: deploy in one command |
| 49 | +Run from your laptop (replace placeholders): |
51 | 50 |
|
52 | | -This codebase is authored under the goVerta collective and aspires to the production rigor expected of core-infra artifacts. The project is intentionally conservative in feature accretion, biasing toward operability, determinism, and testable failure semantics over breadth. |
| 51 | +```bash |
| 52 | +launchd deploy \ |
| 53 | + --host <server-ip-or-dns> \ |
| 54 | + --user <ssh-username> \ |
| 55 | + --app ./path/to/your/app \ |
| 56 | + --port <port> \ |
| 57 | + --timeout 60s |
| 58 | +``` |
53 | 59 |
|
54 | | -## Contributors and Roles |
| 60 | +What happens: |
| 61 | +1) Your app is compiled. |
| 62 | +2) The binary is copied to `/usr/local/bin/<app>` on the server. |
| 63 | +3) A systemd unit `<app>.service` is created/updated and (re)started. |
| 64 | +4) Optional migrations run if configured. |
| 65 | +5) Launchd waits for `http://<host>:<port>/health` to be OK. |
| 66 | + |
| 67 | +On success, your service is enabled on boot and running under systemd. |
| 68 | + |
| 69 | +## How it works (simple explanation) |
| 70 | +- Build: uses your local Go toolchain to compile your app. |
| 71 | +- Transfer: uses `scp` over SSH to put the binary on the server. |
| 72 | +- Service: writes a `.service` file, runs `systemctl daemon-reload`, `enable`, and `restart`. |
| 73 | +- Migrate (optional): calls a migration tool if you have one installed on the server. |
| 74 | +- Health check: polls your `/health` endpoint until it responds OK or times out. |
| 75 | + |
| 76 | +## Common problems and fixes |
| 77 | +- SSH fails: verify `ssh <user>@<host>` works and keys/Passwords are set up. |
| 78 | +- Sudo prompts: ensure your SSH user can run the necessary `systemctl` and file copy with `sudo`. |
| 79 | +- Port in use: stop whatever is using that port or change `--port`. |
| 80 | +- Health check fails: make sure your service starts quickly, listens on the right port, and returns 200 on `/health`. |
| 81 | +- See logs: `ssh <user>@<host> 'sudo journalctl -u <app>.service -f'`. |
| 82 | + |
| 83 | +## Remove or stop the service (on the server) |
| 84 | +```bash |
| 85 | +sudo systemctl stop <app>.service |
| 86 | +sudo systemctl disable <app>.service |
| 87 | +sudo rm -f /usr/local/bin/<app> |
| 88 | +sudo rm -f /etc/systemd/system/<app>.service |
| 89 | +sudo systemctl daemon-reload |
| 90 | +``` |
55 | 91 |
|
56 | | -- Saad H. Tiwana — lead author, deployment pipeline, systemd strategy, reliability posture |
57 | | - GitHub: https://github.com/saadhtiwana |
58 | | -- Ahmad Mustafa — SSH/SCP transport hardening, testing harnesses |
59 | | - GitHub: https://github.com/ahmadmustafa02 |
60 | | -- Majid Farooq Qureshi — QA and Toolsmith, Makefile/CI touchpoints, documentation QA |
61 | | - GitHub: https://github.com/Majid-Farooq-Qureshi |
| 92 | +## Safety and re-runs |
| 93 | +You can run the same deploy command again. It will overwrite the binary, refresh the unit, and restart safely. This is called idempotent behavior. |
62 | 94 |
|
63 | | -Saad authored the core code; Ahmad and Majid contributed engineering and QA functions across transport, tests, and docs. |
| 95 | +## FAQ |
| 96 | +- Do I need Docker? No. |
| 97 | +- Do I need Go on the server? No, only on your laptop (build happens locally). |
| 98 | +- Do I need root? You need `sudo` to install the binary and manage systemd. |
64 | 99 |
|
65 | | -— saad and gang is who build this |
| 100 | +Saad and gang is who build this |
0 commit comments