# MotorSignal Operations Actions — Architecture & Security Model ## 1. Overview The Operations Action subsystem provides authenticated, audited, and strictly bounded execution of administrative operations on the MotorSignal production infrastructure (`api.motorsignal.app`). Supported Actions: - `deploy_website`: Fetch `origin/master`, verify clean state, build Next.js release in staging, atomically swap symlink, restart website, and verify health with automatic rollback. - `restart_website`: Restart `motorsignal-website.service` and verify loopback and public endpoints. - `restart_api`: Detached restart of `motorsignal-api.service` via `systemd-run`, with pre-committed action audit and startup reconciliation. - `reboot_server`: Detached system reboot via `systemd-run`, requiring explicit typed confirmation `"REBOOT MOTORSIGNAL SERVER"`. --- ## 2. Security Boundaries & Threat Model ### Principle of Least Privilege - The web and API processes (`motorsignal` service user) do **NOT** have unrestricted root access or arbitrary sudo permissions. - Sudo access is strictly constrained to a single canonical, root-owned, non-writable script: `/opt/motorsignal/current/backend/scripts/ops-action-helper.sh` - Sudoers policy (`/etc/sudoers.d/motorsignal-ops-actions`): `motorsignal ALL=(root) NOPASSWD: /opt/motorsignal/current/backend/scripts/ops-action-helper.sh` - The helper script rejects: - Wildcard arguments - Shell string interpolation - Arbitrary paths, branches, git remotes, or environment flags - Free-form user text ### Non-Interactive Subprocess Execution - In Python, all execution uses `subprocess.run(..., shell=False)` with an explicit argv array `[helper_path, action_type]`. - Output is sanitized: raw exceptions, filesystem paths, passwords, and tokens are scrubbed before storage or display. ### Role-Based Access Control - All action invocations require the `owner` role via `require_ops_roles("owner")`. - `operator` role users have read-only access to `/api/v1/ops/actions` (history and status). - Unauthenticated requests receive HTTP 401; unauthorized requests receive HTTP 403. ### Mutual Exclusion & Concurrency Control - Only one infrastructure mutation may execute at a time. - The service queries for active actions in `('queued', 'preflight', 'running', 'verifying', 'rollback_running')`. - If an active action exists, subsequent requests are rejected immediately with HTTP 409 Conflict. ### Idempotency - Requests may supply an `idempotency_key` (8–128 characters). - Enforced at the database level via a unique partial index on `(requested_by_id, action_type, idempotency_key)`. - Repeated requests return the existing action record without re-executing. ### Typed Confirmations - `restart_api` requires `confirmation_text == "RESTART API"`. - `reboot_server` requires `confirmation_text == "REBOOT MOTORSIGNAL SERVER"`.