Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b1ad5ca82 |
135
tools/mos-comms/MOS-COMMS-PROTOCOL.md
Normal file
135
tools/mos-comms/MOS-COMMS-PROTOCOL.md
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
# Mos Cross-Agent Git Communications Protocol
|
||||||
|
|
||||||
|
`mos-comms.sh` is a portable, append-only relay for orchestrators on separate
|
||||||
|
hosts. It uses a git repository supplied by the operator as transport. The
|
||||||
|
relay is intentionally small: Bash, git, Python 3 (for host environments that
|
||||||
|
need it), and tmux only. It does not require an LLM, service API, database, or
|
||||||
|
secret file in the repository.
|
||||||
|
|
||||||
|
## Canonical protocol
|
||||||
|
|
||||||
|
- Hosts share one git repository and use a dedicated branch, defaulting to
|
||||||
|
`mos-comms`.
|
||||||
|
- Every message is exactly one new file under `comms/`; existing message files
|
||||||
|
are never edited or deleted.
|
||||||
|
- A sender writes
|
||||||
|
`comms/<UTC>__from-<AGENT_NAME>__<random>.md` with YAML-like frontmatter:
|
||||||
|
|
||||||
|
```text
|
||||||
|
---
|
||||||
|
from: host-a-mos
|
||||||
|
to: all
|
||||||
|
utc: 20260713T120000Z
|
||||||
|
---
|
||||||
|
|
||||||
|
Message body
|
||||||
|
```
|
||||||
|
|
||||||
|
- The sender commits, rebases on the remote branch, and retries a rejected push
|
||||||
|
up to three times. Independent files make normal concurrent sends
|
||||||
|
conflict-free.
|
||||||
|
- `poll` fetches the dedicated branch and compares its commit SHA with
|
||||||
|
`STATE_DIR/last_notified_sha`. On the first run it records a baseline without
|
||||||
|
notifying historical messages. Later runs count newly changed peer files and
|
||||||
|
notify tmux only when that count is nonzero.
|
||||||
|
- `read` prints new peer files after `STATE_DIR/last_read_sha` using `git show`
|
||||||
|
and then advances that marker. Message bodies surface here as data, not code.
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mos-comms.sh setup
|
||||||
|
mos-comms.sh send 'A deliberate message for peers'
|
||||||
|
mos-comms.sh poll
|
||||||
|
mos-comms.sh read
|
||||||
|
mos-comms.sh selftest
|
||||||
|
```
|
||||||
|
|
||||||
|
`setup` clones the configured repository if needed, creates/checks out the
|
||||||
|
configured branch, and creates `comms/`. The first sender creates the remote
|
||||||
|
branch if it is not present yet.
|
||||||
|
|
||||||
|
`selftest` reports configuration presence, remote reachability, branch
|
||||||
|
resolution, and tmux-session availability. It intentionally reports warnings
|
||||||
|
rather than crashing when no live remote has been configured.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
The script sources `$MOS_COMMS_CONFIG` when set; otherwise it uses
|
||||||
|
`~/.config/mos-comms/mos-comms.config`. Copy
|
||||||
|
`mos-comms.config.example` to that path and set `AGENT_NAME` and
|
||||||
|
`COMMS_REMOTE` locally. Defaults are:
|
||||||
|
|
||||||
|
| Variable | Default |
|
||||||
|
| --- | --- |
|
||||||
|
| `COMMS_REPO_DIR` | `~/.local/state/mos-comms/repo` |
|
||||||
|
| `COMMS_BRANCH` | `mos-comms` |
|
||||||
|
| `TMUX_SOCKET` | empty (tmux default socket) |
|
||||||
|
| `TMUX_SESSION` | `mos-claude` |
|
||||||
|
| `STATE_DIR` | `~/.local/state/mos-comms` |
|
||||||
|
|
||||||
|
`GIT_CRED_HELPER` is optional. If configured, every git operation runs with
|
||||||
|
`git -c credential.helper=$GIT_CRED_HELPER`. Credential values do not belong in
|
||||||
|
the config or repository.
|
||||||
|
|
||||||
|
## Threat model
|
||||||
|
|
||||||
|
1. **Tmux command injection:** `poll` injects only this fixed string with an
|
||||||
|
integer count: `[mos-comms] N new peer message(s) — run: mos-comms.sh read`.
|
||||||
|
It never interpolates a message body, filename, or author into `send-keys`.
|
||||||
|
A crafted message cannot become keystrokes.
|
||||||
|
2. **Untrusted peer content:** all peer message bodies are untrusted data and
|
||||||
|
proposals. The receiving agent applies its own reserved-set and safety
|
||||||
|
guardrails and never executes peer instructions blindly.
|
||||||
|
3. **No mechanical auto-reply:** receiving only produces a notification. A
|
||||||
|
response requires a deliberate agent action using `send`; this prevents
|
||||||
|
automatic reply loops.
|
||||||
|
4. **Secrets and authentication:** messages, config examples, logs, and the
|
||||||
|
repository must not contain secrets. Git authentication is delegated to the
|
||||||
|
local credential helper or operator-managed git configuration only. The
|
||||||
|
script does not print credentials.
|
||||||
|
5. **Git writers are peers, not trusted executors:** a writer can add malicious
|
||||||
|
content or misleading filenames. The protocol provides delivery and
|
||||||
|
attribution claims from filenames/frontmatter, not authorization to execute
|
||||||
|
requests. Repository access should be limited to intended relay hosts.
|
||||||
|
|
||||||
|
## Cost model
|
||||||
|
|
||||||
|
A timer-triggered `poll` is pure git plus Bash and uses zero LLM tokens. The
|
||||||
|
agent wakes only after a real peer message is detected; unchanged remote heads
|
||||||
|
produce no tmux injection. Reading and replying are deliberate operations.
|
||||||
|
|
||||||
|
## New-host install
|
||||||
|
|
||||||
|
1. Copy the package to a local directory and install the executable:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p ~/.local/bin ~/.config/mos-comms ~/.config/systemd/user
|
||||||
|
cp mos-comms.sh ~/.local/bin/mos-comms.sh
|
||||||
|
chmod 700 ~/.local/bin/mos-comms.sh
|
||||||
|
cp mos-comms.config.example ~/.config/mos-comms/mos-comms.config
|
||||||
|
chmod 600 ~/.config/mos-comms/mos-comms.config
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Edit the **local-only** config. Set a unique `AGENT_NAME` and the
|
||||||
|
operator-provisioned `COMMS_REMOTE`. Configure git credentials separately
|
||||||
|
(or set a credential-helper name, never a token value).
|
||||||
|
3. Confirm prerequisites and create the local checkout:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~/.local/bin/mos-comms.sh selftest
|
||||||
|
~/.local/bin/mos-comms.sh setup
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Install and enable the per-user timer:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp systemd/mos-comms.service systemd/mos-comms.timer ~/.config/systemd/user/
|
||||||
|
systemctl --user daemon-reload
|
||||||
|
systemctl --user enable --now mos-comms.timer
|
||||||
|
systemctl --user list-timers mos-comms.timer
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Send a deliberate test message from one host, then run `read` on the other
|
||||||
|
host after its poll notification. Keep the relay branch dedicated to this
|
||||||
|
protocol; do not share it with source-code work.
|
||||||
23
tools/mos-comms/mos-comms.config.example
Normal file
23
tools/mos-comms/mos-comms.config.example
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Copy to ~/.config/mos-comms/mos-comms.config (mode 600) or point
|
||||||
|
# MOS_COMMS_CONFIG at another local-only file. Do not commit this file with
|
||||||
|
# operator values or credentials.
|
||||||
|
|
||||||
|
# Required deployment identity; letters, numbers, dots, and hyphens only.
|
||||||
|
AGENT_NAME="host-a-mos"
|
||||||
|
|
||||||
|
# Required: URL/path of the shared, operator-provisioned git repository.
|
||||||
|
# Authentication is delegated to git or the optional credential helper below.
|
||||||
|
COMMS_REMOTE=""
|
||||||
|
|
||||||
|
# Optional local paths and branch.
|
||||||
|
COMMS_REPO_DIR="$HOME/.local/state/mos-comms/repo"
|
||||||
|
COMMS_BRANCH="mos-comms"
|
||||||
|
STATE_DIR="$HOME/.local/state/mos-comms"
|
||||||
|
|
||||||
|
# Empty selects tmux's default socket. Set only when a named socket is used.
|
||||||
|
TMUX_SOCKET=""
|
||||||
|
TMUX_SESSION="mos-claude"
|
||||||
|
|
||||||
|
# Optional git credential-helper name/path. Never place credential values here.
|
||||||
|
# Example: GIT_CRED_HELPER="cache"
|
||||||
|
GIT_CRED_HELPER=""
|
||||||
291
tools/mos-comms/mos-comms.sh
Executable file
291
tools/mos-comms/mos-comms.sh
Executable file
@@ -0,0 +1,291 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Portable, append-only git relay for deliberate cross-agent communication.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
CONFIG_PATH="${MOS_COMMS_CONFIG:-$HOME/.config/mos-comms/mos-comms.config}"
|
||||||
|
CONFIG_PRESENT=0
|
||||||
|
if [[ -f "$CONFIG_PATH" ]]; then
|
||||||
|
# Configuration is local operator input. It must not be committed to the relay repo.
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
source "$CONFIG_PATH"
|
||||||
|
CONFIG_PRESENT=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
AGENT_NAME="${AGENT_NAME:-}"
|
||||||
|
COMMS_REMOTE="${COMMS_REMOTE:-}"
|
||||||
|
COMMS_REPO_DIR="${COMMS_REPO_DIR:-$HOME/.local/state/mos-comms/repo}"
|
||||||
|
COMMS_BRANCH="${COMMS_BRANCH:-mos-comms}"
|
||||||
|
TMUX_SOCKET="${TMUX_SOCKET:-}"
|
||||||
|
TMUX_SESSION="${TMUX_SESSION:-mos-claude}"
|
||||||
|
STATE_DIR="${STATE_DIR:-$HOME/.local/state/mos-comms}"
|
||||||
|
GIT_CRED_HELPER="${GIT_CRED_HELPER:-}"
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'EOF'
|
||||||
|
Usage: mos-comms.sh setup | poll | read | send <text> | selftest
|
||||||
|
|
||||||
|
Messages are append-only files on the configured shared git branch. `poll` is
|
||||||
|
mechanical: it only checks git and emits a fixed tmux notification.
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
die() {
|
||||||
|
printf 'mos-comms: %s\n' "$*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
warn() {
|
||||||
|
printf 'mos-comms: warning: %s\n' "$*" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
gitc() {
|
||||||
|
if [[ -n "$GIT_CRED_HELPER" ]]; then
|
||||||
|
git -c credential.helper="$GIT_CRED_HELPER" "$@"
|
||||||
|
else
|
||||||
|
git "$@"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
require_identity() {
|
||||||
|
[[ -n "$AGENT_NAME" ]] || die 'AGENT_NAME is required in the local config'
|
||||||
|
[[ "$AGENT_NAME" =~ ^[A-Za-z0-9.-]+$ ]] || die 'AGENT_NAME may contain only letters, numbers, dots, and hyphens'
|
||||||
|
}
|
||||||
|
|
||||||
|
require_remote() {
|
||||||
|
[[ -n "$COMMS_REMOTE" ]] || die 'COMMS_REMOTE is required in the local config'
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_state_dir() {
|
||||||
|
mkdir -p "$STATE_DIR"
|
||||||
|
chmod 700 "$STATE_DIR"
|
||||||
|
}
|
||||||
|
|
||||||
|
remote_branch_exists() {
|
||||||
|
gitc -C "$COMMS_REPO_DIR" show-ref --verify --quiet "refs/remotes/origin/$COMMS_BRANCH"
|
||||||
|
}
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
require_remote
|
||||||
|
require_identity
|
||||||
|
mkdir -p "$(dirname "$COMMS_REPO_DIR")"
|
||||||
|
|
||||||
|
if [[ ! -d "$COMMS_REPO_DIR/.git" ]]; then
|
||||||
|
gitc clone "$COMMS_REMOTE" "$COMMS_REPO_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
gitc -C "$COMMS_REPO_DIR" remote set-url origin "$COMMS_REMOTE"
|
||||||
|
gitc -C "$COMMS_REPO_DIR" fetch origin
|
||||||
|
if remote_branch_exists; then
|
||||||
|
# Preserve any local append-only commit that is awaiting a retry; do not
|
||||||
|
# reset a checked-out branch to origin here.
|
||||||
|
if gitc -C "$COMMS_REPO_DIR" show-ref --verify --quiet "refs/heads/$COMMS_BRANCH"; then
|
||||||
|
gitc -C "$COMMS_REPO_DIR" checkout "$COMMS_BRANCH"
|
||||||
|
else
|
||||||
|
gitc -C "$COMMS_REPO_DIR" checkout --track -b "$COMMS_BRANCH" "origin/$COMMS_BRANCH"
|
||||||
|
fi
|
||||||
|
gitc -C "$COMMS_REPO_DIR" branch --set-upstream-to="origin/$COMMS_BRANCH" "$COMMS_BRANCH"
|
||||||
|
else
|
||||||
|
gitc -C "$COMMS_REPO_DIR" checkout -B "$COMMS_BRANCH"
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$COMMS_REPO_DIR/comms"
|
||||||
|
ensure_state_dir
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_remote_branch() {
|
||||||
|
if ! remote_branch_exists; then
|
||||||
|
warn "origin branch '$COMMS_BRANCH' does not exist yet"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
message_is_from_self() {
|
||||||
|
local file="$1"
|
||||||
|
[[ "$file" == *"__from-${AGENT_NAME}__"* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
changed_message_files() {
|
||||||
|
local from_sha="$1"
|
||||||
|
local to_sha="$2"
|
||||||
|
if [[ -n "$from_sha" ]] && gitc -C "$COMMS_REPO_DIR" cat-file -e "$from_sha^{commit}" 2>/dev/null; then
|
||||||
|
gitc -C "$COMMS_REPO_DIR" diff --name-only "$from_sha" "$to_sha" -- comms/
|
||||||
|
else
|
||||||
|
gitc -C "$COMMS_REPO_DIR" ls-tree -r --name-only "$to_sha" -- comms/
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
tmuxc() {
|
||||||
|
if [[ -n "$TMUX_SOCKET" ]]; then
|
||||||
|
tmux -L "$TMUX_SOCKET" "$@"
|
||||||
|
else
|
||||||
|
tmux "$@"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
send() {
|
||||||
|
require_identity
|
||||||
|
require_remote
|
||||||
|
[[ $# -ge 1 ]] || die 'send requires message text'
|
||||||
|
setup
|
||||||
|
|
||||||
|
local body="$*"
|
||||||
|
local utc filename path
|
||||||
|
utc="$(date -u +%Y%m%dT%H%M%SZ)"
|
||||||
|
filename="${utc}__from-${AGENT_NAME}__${RANDOM}${RANDOM}.md"
|
||||||
|
path="$COMMS_REPO_DIR/comms/$filename"
|
||||||
|
umask 077
|
||||||
|
{
|
||||||
|
printf '%s\n' '---'
|
||||||
|
printf 'from: %s\n' "$AGENT_NAME"
|
||||||
|
printf '%s\n' 'to: all'
|
||||||
|
printf 'utc: %s\n' "$utc"
|
||||||
|
printf '%s\n\n' '---'
|
||||||
|
printf '%s\n' "$body"
|
||||||
|
} >"$path"
|
||||||
|
|
||||||
|
gitc -C "$COMMS_REPO_DIR" add -- "comms/$filename"
|
||||||
|
if ! gitc -C "$COMMS_REPO_DIR" config user.name >/dev/null; then
|
||||||
|
gitc -C "$COMMS_REPO_DIR" config user.name "$AGENT_NAME mos-comms"
|
||||||
|
fi
|
||||||
|
if ! gitc -C "$COMMS_REPO_DIR" config user.email >/dev/null; then
|
||||||
|
gitc -C "$COMMS_REPO_DIR" config user.email "$AGENT_NAME@localhost"
|
||||||
|
fi
|
||||||
|
gitc -C "$COMMS_REPO_DIR" commit -m "comms: ${AGENT_NAME} ${utc}"
|
||||||
|
|
||||||
|
local attempt
|
||||||
|
for attempt in 1 2 3; do
|
||||||
|
if remote_branch_exists; then
|
||||||
|
gitc -C "$COMMS_REPO_DIR" pull --rebase origin "$COMMS_BRANCH"
|
||||||
|
fi
|
||||||
|
if gitc -C "$COMMS_REPO_DIR" push origin "HEAD:refs/heads/$COMMS_BRANCH"; then
|
||||||
|
printf 'sent: %s\n' "$filename"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [[ "$attempt" -lt 3 ]]; then
|
||||||
|
warn "push rejected; retrying ($attempt/3)"
|
||||||
|
gitc -C "$COMMS_REPO_DIR" fetch origin
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
die 'push was rejected after 3 attempts; local message commit remains available for retry'
|
||||||
|
}
|
||||||
|
|
||||||
|
poll() {
|
||||||
|
require_identity
|
||||||
|
require_remote
|
||||||
|
setup
|
||||||
|
gitc -C "$COMMS_REPO_DIR" fetch origin
|
||||||
|
ensure_remote_branch || return 0
|
||||||
|
|
||||||
|
local head marker last_notified
|
||||||
|
head="$(gitc -C "$COMMS_REPO_DIR" rev-parse "origin/$COMMS_BRANCH")"
|
||||||
|
marker="$STATE_DIR/last_notified_sha"
|
||||||
|
if [[ ! -f "$marker" ]]; then
|
||||||
|
printf '%s\n' "$head" >"$marker"
|
||||||
|
printf 'poll: baseline recorded; existing history was not notified\n'
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
last_notified="$(<"$marker")"
|
||||||
|
if [[ "$last_notified" == "$head" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local file count=0
|
||||||
|
while IFS= read -r file; do
|
||||||
|
[[ -n "$file" ]] || continue
|
||||||
|
if ! message_is_from_self "$file"; then
|
||||||
|
((count += 1))
|
||||||
|
fi
|
||||||
|
done < <(changed_message_files "$last_notified" "$head")
|
||||||
|
|
||||||
|
if ((count > 0)); then
|
||||||
|
# Intentionally static except for an integer: never inject peer data into tmux.
|
||||||
|
tmuxc send-keys -t "$TMUX_SESSION" "[mos-comms] $count new peer message(s) — run: mos-comms.sh read"
|
||||||
|
tmuxc send-keys -t "$TMUX_SESSION" Enter
|
||||||
|
fi
|
||||||
|
printf '%s\n' "$head" >"$marker"
|
||||||
|
}
|
||||||
|
|
||||||
|
read_messages() {
|
||||||
|
require_identity
|
||||||
|
require_remote
|
||||||
|
setup
|
||||||
|
gitc -C "$COMMS_REPO_DIR" fetch origin
|
||||||
|
ensure_remote_branch || return 0
|
||||||
|
|
||||||
|
local head marker last_read file found=0
|
||||||
|
head="$(gitc -C "$COMMS_REPO_DIR" rev-parse "origin/$COMMS_BRANCH")"
|
||||||
|
marker="$STATE_DIR/last_read_sha"
|
||||||
|
last_read=""
|
||||||
|
if [[ -f "$marker" ]]; then
|
||||||
|
last_read="$(<"$marker")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
while IFS= read -r file; do
|
||||||
|
[[ -n "$file" ]] || continue
|
||||||
|
if message_is_from_self "$file"; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
found=1
|
||||||
|
printf '%s\n' "===== $file ====="
|
||||||
|
# Peer content is printed as data. It is never evaluated or sent to tmux.
|
||||||
|
gitc -C "$COMMS_REPO_DIR" show "origin/$COMMS_BRANCH:$file"
|
||||||
|
printf '\n'
|
||||||
|
done < <(changed_message_files "$last_read" "$head")
|
||||||
|
|
||||||
|
printf '%s\n' "$head" >"$marker"
|
||||||
|
if ((found == 0)); then
|
||||||
|
printf 'No new peer messages.\n'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
selftest() {
|
||||||
|
local remote_ok=0 branch_ok=0 tmux_ok=0 config_ok=0
|
||||||
|
if ((CONFIG_PRESENT == 1)); then
|
||||||
|
printf 'PASS config present: %s\n' "$CONFIG_PATH"
|
||||||
|
config_ok=1
|
||||||
|
else
|
||||||
|
printf 'FAIL config present: %s\n' "$CONFIG_PATH"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$COMMS_REMOTE" ]]; then
|
||||||
|
printf 'WARN remote reachable: COMMS_REMOTE is not configured\n'
|
||||||
|
elif gitc ls-remote "$COMMS_REMOTE" >/dev/null 2>&1; then
|
||||||
|
printf 'PASS remote reachable\n'
|
||||||
|
remote_ok=1
|
||||||
|
else
|
||||||
|
printf 'FAIL remote reachable\n'
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ((remote_ok == 1)) && gitc ls-remote --exit-code --heads "$COMMS_REMOTE" "$COMMS_BRANCH" >/dev/null 2>&1; then
|
||||||
|
printf 'PASS branch resolves: %s\n' "$COMMS_BRANCH"
|
||||||
|
branch_ok=1
|
||||||
|
elif [[ -z "$COMMS_REMOTE" ]]; then
|
||||||
|
printf 'WARN branch resolves: skipped without a remote\n'
|
||||||
|
else
|
||||||
|
printf 'FAIL branch resolves: %s\n' "$COMMS_BRANCH"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if tmuxc has-session -t "$TMUX_SESSION" 2>/dev/null; then
|
||||||
|
printf 'PASS tmux session exists: %s\n' "$TMUX_SESSION"
|
||||||
|
tmux_ok=1
|
||||||
|
else
|
||||||
|
printf 'FAIL tmux session exists: %s\n' "$TMUX_SESSION"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Report all checks without making a no-remote first-run unusable.
|
||||||
|
if ((config_ok == 1 && remote_ok == 1 && branch_ok == 1 && tmux_ok == 1)); then
|
||||||
|
printf 'PASS selftest complete\n'
|
||||||
|
else
|
||||||
|
printf 'FAIL selftest incomplete (see checks above)\n'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
case "${1:-}" in
|
||||||
|
setup) [[ $# -eq 1 ]] || usage; setup ;;
|
||||||
|
poll) [[ $# -eq 1 ]] || usage; poll ;;
|
||||||
|
read) [[ $# -eq 1 ]] || usage; read_messages ;;
|
||||||
|
send) shift; send "$@" ;;
|
||||||
|
selftest) [[ $# -eq 1 ]] || usage; selftest ;;
|
||||||
|
-h|--help|help) usage ;;
|
||||||
|
*) usage; exit 2 ;;
|
||||||
|
esac
|
||||||
6
tools/mos-comms/systemd/mos-comms.service
Normal file
6
tools/mos-comms/systemd/mos-comms.service
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Mos cross-host git communications poll
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=%h/.local/bin/mos-comms.sh poll
|
||||||
9
tools/mos-comms/systemd/mos-comms.timer
Normal file
9
tools/mos-comms/systemd/mos-comms.timer
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Poll Mos cross-host git communications
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
OnBootSec=2min
|
||||||
|
OnUnitActiveSec=15min
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
||||||
Reference in New Issue
Block a user