Mobile Agent Ops — Moshi + NAS¶
Authority: Operator Playbook (Engineering Standard)
Host: blueflynas.tailcf98b3.ts.net (Synology DS224+, Tailscale)
Mobile surface: Moshi (upstream — do not fork)
Roles¶
| Surface | Role |
|---|---|
| Moshi (iPhone/iPad) | Mobile operator: SSH/Mosh terminal, inbox, Live Activity, approvals |
NAS (agent-ops container) |
Persistent babysitting host: tmux agent-ops, agent CLIs, moshi-hook serve |
| Mac M4 | Validate only: DDEV, local IDE, MR prep — not primary babysitting host |
| code-server | Browser lane: https://code.blueflyagents.com / NAS :8080 |
| Gas City (Oracle) | Execution SoR — read-only on phone via gateway HTTP, never raw gt/bd |
Connect from phone (Moshi)¶
- Install Moshi; join Tailscale.
- Saved host:
blueflynas.tailcf98b3.ts.net, port 2222, user agent, Mosh enabled. - Pair hooks: Moshi app → Settings → Hooks → copy token; on NAS container:
moshi-hook pair --token <TOKEN> moshi-hook install moshi-hook serve - Attach tmux:
mosh --ssh="ssh -p 2222" [email protected] -- "tmux attach -t agent-ops" - Host status dot in Moshi must be green (
moshi-hook serveon gateway127.0.0.1:24543).
blu-cli (Mac / NAS)¶
blu-remote connect # mosh + Moshi instructions
blu-remote moshi-doctor # upstream moshi-hook status
blu-remote ops # agent-ops: shell | A2A | MCP | gascity-status
blu-remote gascity-status # read-only gateway JSON
Defaults (override with env):
BLU_REMOTE_MOSH_HOST=blueflynas.tailcf98b3.ts.netBLU_REMOTE_MOSH_PORT=2222BLU_REMOTE_MOSH_USER=agentBLU_REMOTE_TMUX_SESSION=agent-opsGASTOWN_GATEWAY_BASE_URL=https://gascity.blueflyagents.com
Hook stack (layering)¶
| Layer | Config | Purpose |
|---|---|---|
| User-global | moshi-hook in ~/.claude, ~/.cursor, ~/.codex |
Moshi inbox, diff, Live Activity |
| Project | .cursor/hooks.json, .codex/hooks.json |
gt-policy, bd codex-hook |
Do not remove Bluefly project hooks when installing Moshi; Moshi docs preserve user-owned entries.
Preflight (before structural work)¶
git status
qmd status
blu-remote moshi-doctor
See: BluCity-Docs/lanes/gascity-blu-realignment/gascity/conflicts/gt-bd-qmd-git-preflight.md
NAS babysitting container¶
Runtime path on NAS: ~/deploy/agent-ops/ (Docker Compose).
export PATH="/var/packages/ContainerManager/target/usr/bin:$PATH"
cd ~/deploy/agent-ops && docker compose up -d
Volumes: /volume1/AgentPlatform → /workspace
What phone must NOT do¶
gt convoy activate,bd create, or other Gas City mutators- Treat tmux sessions as work authority (Oracle
~/gt+ beads is SoR)
Termius¶
Deprecated fallback. Use Moshi saved hosts. Legacy: blu-remote termius (deprecated).
Troubleshooting¶
| Symptom | Fix |
|---|---|
| Moshi dot hollow/grey | Install moshi-hook on babysitting host |
| Moshi dot grey not running | moshi-hook serve (or restart container entrypoint) |
| Moshi dot amber | brew upgrade moshi-hook / re-run NAS install script |
| Unpaired | moshi-hook pair --token … from Moshi app |
| SSH denied | Use bluefly@blueflynas for DSM; agent@:2222 for babysitting container |
| No iPhone push | Pairing required; custom watcher removed — use Moshi only |
Skill (upstream)¶
npx skills add rjyo/moshi-skill -g -a claude-code -a cursor -y
Claude Code hook wiring (verified 2026-07-30)¶
~/.claude/settings.json (user-global, per the layering table above) wires moshi-hook claude-hook into six Claude Code lifecycle events:
| Event | Async | Purpose |
|---|---|---|
PermissionRequest |
sync | Routes the pending tool-call approval through Moshi (phone tap) before Claude Code proceeds |
PreToolUse (matcher: AskUserQuestion, ExitPlanMode) |
async | Notify-only |
PostToolUse (matcher: AskUserQuestion, ExitPlanMode) |
async | Notify-only |
SessionStart / SessionEnd / Stop |
async | Lifecycle notifications (Live Activity, inbox) |
Only PermissionRequest is synchronous — it blocks the tool call until Moshi returns a decision. Everything else is fire-and-forget notification.
Execution flow (traced from moshi-hook --help + settings.json; local leg VERIFIED, cloud/phone leg INFERRED from the app's documented feature set — inbox/Live Activity/approvals)¶
Claude Code tool call
→ PermissionRequest hook fires (sync)
→ moshi-hook claude-hook (local binary, reads the request over stdin/args)
→ local Unix socket → moshi-hook serve daemon
→ (if paired) persistent WebSocket → Moshi backend (getmoshi.app)
→ push notification / Live Activity → phone
→ operator taps approve/deny
→ decision flows back over the same WebSocket → socket → claude-hook exit code
→ Claude Code allow/deny → tool executes or is blocked
moshi-hook serve additionally exposes a local HTTP gateway (--gateway-listen) — this is the 127.0.0.1:24543 status endpoint the "Host status dot" in the Moshi app polls (see Connect-from-phone step 5).
Known failure mode: unattended sessions have no approver¶
PermissionRequest is synchronous and requires a human to tap approve/deny on the paired phone. In a background/unattended session (no operator present to receive the push), every gated Bash call — including read-only ones — denies or times out with no retry path, since Claude Code correctly avoids re-issuing an identical denied call. Observed directly in a background session on 2026-07-30: git commit, and later plain ls/find probes of the same host, were denied back-to-back with no operator interaction. This is a structural consequence of Moshi's design center (a human with a phone), not a Moshi defect — background/CI-style agent sessions are a use case the pairing model doesn't cover today.
Engineering integration assessment (2026-07-30)¶
Moshi is upstream, closed-source, do-not-fork (per the Authority line at the top of this doc) — a commercial mobile app, not a Bluefly-owned service. That bounds every recommendation below: Bluefly can integrate at the edges Moshi already exposes (CLI hooks, local socket/gateway, the project-hook layer sitting alongside it), not by modifying Moshi's own approval-routing or notification backend.
CURRENT
- Local daemon + CLI (moshi-hook), Unix socket, optional local HTTP gateway, WebSocket bridge to a hosted backend.
- Approval delivery is phone-only (push + Live Activity) via the paired Moshi app.
- Bluefly's own hook layer (gt-policy, bd codex-hook via .cursor/hooks.json/.codex/hooks.json) already sits alongside Moshi's user-global hooks without conflict (see Hook stack table) — this is the existing, correct seam for Bluefly-side additions.
- No Bead/receipt integration exists today. No policy-based auto-approve exists today. Routing is phone-only — no Telegram/Slack/Synology Chat/BLU/OpenClaw destination exists today.
RECOMMENDED (buildable by Bluefly, at the existing seam, without touching Moshi)
- Add a project-level PostToolUse/Stop hook (same layer as gt-policy) that writes a five-field receipt or Bead entry for every tool call Moshi's PermissionRequest approved/denied — turns the approval event into a governed audit record without needing anything from Moshi itself.
- For unattended/background sessions specifically: don't route around PermissionRequest — instead, have the launching side (whatever starts a background job) pre-flight-check moshi-hook status/pairing liveness and refuse to start unattended work that will need approvals no one can grant, surfacing that as a scheduling decision rather than a silent per-call deny.
BLOCKERS
- Cannot add alternate approval destinations (Telegram, Synology Chat, Slack, BLU, OpenClaw) inside Moshi's routing — that requires a Moshi product feature, not something this repo can build. Would need to be a feature request to the vendor, not an integration task here.
- Cannot make approvals policy-driven (auto-approve low-risk, escalate production) inside Moshi — same reason. Policy-based gating already exists at the Bluefly layer (gt-policy) for mutating tools in Gas City workspaces; it is independent of and does not currently coordinate with Moshi's approve/deny.
- Cannot verify or change the WebSocket approval protocol — closed, no source access.
- "Single execution-control plane" spanning Oracle/NAS/BLU/OpenClaw/Gas City/Gas City/GitLab/CI/Beads/Cedar/OSSA cannot include Moshi's own decision engine as a component under Bluefly control — only the client-side hook/CLI surface is ours to extend.
OPPORTUNITIES
- moshi-hook exposes --base-url globally (override Moshi API base URL) — unconfirmed whether Moshi offers a self-hosted/enterprise backend mode; worth a direct question to the vendor rather than assuming either way.
- The existing project-hook layer (gt-policy) is already the right integration point for Bead receipts and policy — extending that layer (not Moshi) is how "policy-driven approval" and "Bead receipts" actually get built.