{"id":51438552,"url":"https://github.com/nerve-ink/nerve-cli","last_synced_at":"2026-07-05T09:30:24.769Z","repository":{"id":364434167,"uuid":"1250749806","full_name":"nerve-ink/nerve-cli","owner":"nerve-ink","description":"Command-line sender for Nerve encrypted ops signals.","archived":false,"fork":false,"pushed_at":"2026-06-13T02:15:07.000Z","size":18,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-13T03:08:54.038Z","etag":null,"topics":["alerts","cicd","cli","devops","e2e-encryption","golang","ios"],"latest_commit_sha":null,"homepage":"https://nerve.ink","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nerve-ink.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-26T23:55:18.000Z","updated_at":"2026-06-13T02:15:10.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/nerve-ink/nerve-cli","commit_stats":null,"previous_names":["nerve-ink/nerve-cli"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/nerve-ink/nerve-cli","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nerve-ink%2Fnerve-cli","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nerve-ink%2Fnerve-cli/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nerve-ink%2Fnerve-cli/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nerve-ink%2Fnerve-cli/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nerve-ink","download_url":"https://codeload.github.com/nerve-ink/nerve-cli/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nerve-ink%2Fnerve-cli/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35150062,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-05T02:00:06.290Z","response_time":100,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["alerts","cicd","cli","devops","e2e-encryption","golang","ios"],"created_at":"2026-07-05T09:30:24.122Z","updated_at":"2026-07-05T09:30:24.756Z","avatar_url":"https://github.com/nerve-ink.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# nerve-cli\n\n[![Website](https://img.shields.io/badge/website-nerve.ink-111214)](https://nerve.ink)\n[![Go Reference](https://pkg.go.dev/badge/github.com/nerve-ink/nerve-cli.svg)](https://pkg.go.dev/github.com/nerve-ink/nerve-cli)\n\n[Website](https://nerve.ink) · [Start](https://nerve.ink/start) · [App Store](https://apps.apple.com/us/app/nerveops/id6778026992) · [Google Play](https://play.google.com/store/apps/details?id=ink.nerve.app\u0026pcampaignid=web_share) · [Run agent](https://github.com/nerve-ink/nerve-agent)\n\nEncrypted CI/CD, cron, webhook and server alerts to iPhone and Android.\n\nSender secrets can only send. Relay sees ciphertext. Actions are optional.\n\n```bash\necho \"deploy failed\" | nerve send\n```\n\n## Start with signals\n\n1. Create a pipe in the Nerve mobile app.\n2. Open **Pipe Setup** and choose **Send signals**.\n3. Copy the sender DSN.\n4. Install `nerve`.\n5. Send your first encrypted alert:\n\n```bash\ncurl -fsSL https://nerve.ink/install.sh | sh\nexport NERVE_DSN=\"nerve://TOKEN:SENDER_KEY@api.nerve.ink\"\necho \"deploy failed\" | nerve send --title \"Nerve test\"\n```\n\nExpected output:\n\n```text\nsent\n```\n\nThe phone receives an encrypted signal. The relay routes ciphertext; decryption\nhappens locally on the device that owns the pipe.\n\nNerveOps is available on the\n[App Store](https://apps.apple.com/us/app/nerveops/id6778026992) and\n[Google Play](https://play.google.com/store/apps/details?id=ink.nerve.app\u0026pcampaignid=web_share).\n\n`nerve send` is the safe first Nerve integration. It reads plaintext from stdin,\nencrypts it locally with the sender key from `NERVE_DSN`, and posts only\nciphertext to the Nerve relay.\n\nThe sender DSN can send signals into one pipe. It cannot read history, decrypt\nold messages, connect as an agent, or execute commands.\n\n## Why this is not just a webhook\n\n| Boundary | What happens |\n|---|---|\n| CI secret leaks | A sender DSN can create alert noise in one pipe. It cannot read history, decrypt content, connect as an agent, or execute commands. |\n| Relay receives traffic | The relay routes encrypted records and delivery metadata. Signal content is encrypted before it reaches the relay. |\n| Phone receives push | APNs/FCM wake or notify the device. Clients sync the encrypted record and decrypt locally. |\n| Agent is needed | Use [`nerve-agent`](https://github.com/nerve-ink/nerve-agent) only when you want signed actions on a machine you control. |\n\n## Copy-paste Examples\n\n- [GitHub Actions failure alert](./examples/github-actions-failure.yml)\n- [Cron backup failure alert](./examples/cron-backup-failed.sh)\n- [systemd failed unit alert](./examples/systemd-failed-unit.sh)\n\n## Install\n\nRecommended:\n\n```bash\ncurl -fsSL https://nerve.ink/install.sh | sh\n```\n\nGo fallback / developer install:\n\n```bash\ngo install github.com/nerve-ink/nerve-cli/cmd/nerve@latest\nexport PATH=\"$PATH:$(go env GOPATH)/bin\"\nnerve --help\n```\n\n## Docker\n\nUse the Docker image when you want `nerve send` in CI, cron, or a host where you\ndo not want to install Go or a local binary.\n\n```bash\necho \"deploy failed\" | docker run -i --rm \\\n  -e NERVE_DSN=\"nerve://TOKEN:SENDER_KEY@api.nerve.ink\" \\\n  p1xel32/nerve-cli:latest send\n```\n\nGitHub Container Registry mirror:\n\n```bash\necho \"deploy failed\" | docker run -i --rm \\\n  -e NERVE_DSN=\"nerve://TOKEN:SENDER_KEY@api.nerve.ink\" \\\n  ghcr.io/nerve-ink/nerve-cli:latest send\n```\n\nThe image is send-only. It does not run an agent and cannot execute commands.\nFor signed actions on a trusted host, use\n[`nerve-agent`](https://github.com/nerve-ink/nerve-agent).\n\n## Flags\n\n```bash\necho \"deploy failed\" | nerve send \\\n  --severity critical \\\n  --title \"Deploy failed\"\n```\n\nCommon flags:\n\n| Flag | Default | Purpose |\n|---|---:|---|\n| `--dsn` | `NERVE_DSN` | Sender DSN. Prefer an env var or CI secret. |\n| `--severity` | `standard` | Signal severity, for example `standard`, `alert`, `critical`. |\n| `--title` | empty | Optional short title shown by clients. |\n| `--kind` | `alert` | Signal kind metadata. |\n| `--timeout` | `10s` | HTTP request timeout. |\n\n## GitHub Actions\n\nStore the sender DSN as a repository or organization secret named `NERVE_DSN`.\n\n```yaml\nname: Notify Nerve\n\non:\n  workflow_run:\n    workflows: [\"Backend Deploy\"]\n    types: [completed]\n\njobs:\n  notify:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Install nerve\n        run: curl -fsSL https://nerve.ink/install.sh | sh\n      - name: Send encrypted deploy signal\n        env:\n          NERVE_DSN: ${{ secrets.NERVE_DSN }}\n        run: |\n          printf 'deploy %s\\nrun: %s\\n' \\\n            \"${{ github.event.workflow_run.conclusion }}\" \\\n            \"${{ github.event.workflow_run.html_url }}\" \\\n            | nerve send --title \"Backend Deploy\"\n```\n\nFor a regular job, send only on failure:\n\n```yaml\n- name: Install nerve\n  run: curl -fsSL https://nerve.ink/install.sh | sh\n- name: Notify Nerve on failure\n  if: failure()\n  env:\n    NERVE_DSN: ${{ secrets.NERVE_DSN }}\n  run: |\n    printf 'FAILED: %s\\nbranch: %s\\nrun: %s\\n' \\\n      \"${{ github.repository }}\" \\\n      \"${{ github.ref_name }}\" \\\n      \"${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\" \\\n      | nerve send --severity critical --title \"CI failed\"\n```\n\nKeep the signal short: repository, branch/environment, status, commit SHA, and\nrun URL. Do not pipe full logs or secrets into phone alerts.\n\n```text\nExample message:\ndeploy success\nsha: b62b038\nrun: https://github.com/nerve-ink/nerve-ops/actions/runs/26969431142\n```\n\n## Cron\n\n```bash\n#!/usr/bin/env bash\nset -euo pipefail\n\nexport NERVE_DSN=\"nerve://TOKEN:SENDER_KEY@api.nerve.ink\"\n\nif ! /opt/jobs/backup.sh; then\n  echo \"backup failed on $(hostname)\" | nerve send --severity alert\n  exit 1\nfi\n```\n\nCrontab:\n\n```cron\n15 3 * * * /opt/jobs/backup-with-nerve.sh\n```\n\n## Bash / Smoke Tests\n\n```bash\nif ./smoke.sh; then\n  echo \"smoke passed: api-prod\" | nerve send\nelse\n  echo \"smoke failed: api-prod\" | nerve send --severity critical\nfi\n```\n\n## Security Model\n\nA sender DSN contains:\n\n- a sender token\n- a sender encryption key\n- the relay host\n\nIf it leaks, an attacker can send fake signals into that one pipe until the\ncredential is rotated.\n\nThey cannot:\n\n- read history\n- decrypt old messages\n- connect as an agent\n- execute commands\n\nDo not put secrets, tokens, private keys, or full environment dumps into signal\ntext. Send short operational context: service, environment, status, commit SHA,\nand a run URL.\n\n## Rotate a Sender DSN\n\n1. Open pipe setup in the mobile app.\n2. Create a new **Send signals** credential.\n3. Update your CI secret or server env var.\n4. Delete the old sender credential.\n\n## Troubleshooting\n\n### `go: command not found`\n\nUse the recommended installer instead of the Go fallback:\n\n```bash\ncurl -fsSL https://nerve.ink/install.sh | sh\n```\n\nThe Go path is only for local development or environments that intentionally\nbuild from source.\n\n### `415 Unsupported Media Type`\n\nYou are probably calling the old `nerve` shell function or a generic webhook\ninstead of the Go CLI. Check:\n\n```bash\ntype nerve\nwhich nerve\nnerve --help\n```\n\nThe current CLI prints `sent` on success and sends\n`Content-Type: application/json` automatically.\n\n### `go install` cannot read the repository\n\nUse the public module path exactly:\n\n```bash\ngo install github.com/nerve-ink/nerve-cli/cmd/nerve@latest\n```\n\nIf Go reports that GitHub needs credentials, verify that your environment is not\nrewriting GitHub URLs to a private mirror or stale local fork.\n\n### `nerve: NERVE_DSN or --dsn is required`\n\nExport the DSN copied from pipe setup:\n\n```bash\nexport NERVE_DSN=\"nerve://TOKEN:SENDER_KEY@api.nerve.ink\"\n```\n\nOr pass it directly:\n\n```bash\necho \"deploy failed\" | nerve send --dsn \"nerve://TOKEN:SENDER_KEY@api.nerve.ink\"\n```\n\n### Signal says `sent`, but I do not see it\n\nCheck that:\n\n- the phone is signed into the same Nerve account\n- notifications are allowed\n- the sender DSN belongs to the pipe you are watching\n- the app can decrypt the pipe keys\n\n## Agent\n\nNeed signed actions from a machine you control? Use\n[`nerve-agent`](https://github.com/nerve-ink/nerve-agent).\n\nThe agent is the advanced path. Start with send-only signals unless you\nexplicitly need remote actions.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnerve-ink%2Fnerve-cli","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnerve-ink%2Fnerve-cli","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnerve-ink%2Fnerve-cli/lists"}