nostr-dev 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. nostr_dev-1.0.0/.env.example +21 -0
  2. nostr_dev-1.0.0/.github/workflows/ci.yml +75 -0
  3. nostr_dev-1.0.0/.gitignore +11 -0
  4. nostr_dev-1.0.0/.pre-commit-config.yaml +20 -0
  5. nostr_dev-1.0.0/CHANGELOG.md +24 -0
  6. nostr_dev-1.0.0/PKG-INFO +254 -0
  7. nostr_dev-1.0.0/README.md +235 -0
  8. nostr_dev-1.0.0/alembic.ini +42 -0
  9. nostr_dev-1.0.0/docker-compose.yml +10 -0
  10. nostr_dev-1.0.0/pyproject.toml +63 -0
  11. nostr_dev-1.0.0/src/nostr_dev/__init__.py +3 -0
  12. nostr_dev-1.0.0/src/nostr_dev/cli.py +62 -0
  13. nostr_dev-1.0.0/src/nostr_dev/commands/__init__.py +1 -0
  14. nostr_dev-1.0.0/src/nostr_dev/commands/common.py +80 -0
  15. nostr_dev-1.0.0/src/nostr_dev/commands/events.py +86 -0
  16. nostr_dev-1.0.0/src/nostr_dev/commands/github.py +483 -0
  17. nostr_dev-1.0.0/src/nostr_dev/commands/keys.py +152 -0
  18. nostr_dev-1.0.0/src/nostr_dev/commands/publish.py +84 -0
  19. nostr_dev-1.0.0/src/nostr_dev/commands/relay.py +184 -0
  20. nostr_dev-1.0.0/src/nostr_dev/config.py +102 -0
  21. nostr_dev-1.0.0/src/nostr_dev/db.py +53 -0
  22. nostr_dev-1.0.0/src/nostr_dev/exceptions.py +47 -0
  23. nostr_dev-1.0.0/src/nostr_dev/logging.py +83 -0
  24. nostr_dev-1.0.0/src/nostr_dev/migrations/env.py +64 -0
  25. nostr_dev-1.0.0/src/nostr_dev/migrations/script.py.mako +25 -0
  26. nostr_dev-1.0.0/src/nostr_dev/migrations/versions/.gitkeep +1 -0
  27. nostr_dev-1.0.0/src/nostr_dev/migrations/versions/41f8dd9a0e37_initial_schema.py +178 -0
  28. nostr_dev-1.0.0/src/nostr_dev/models/__init__.py +15 -0
  29. nostr_dev-1.0.0/src/nostr_dev/models/github.py +66 -0
  30. nostr_dev-1.0.0/src/nostr_dev/models/keys.py +16 -0
  31. nostr_dev-1.0.0/src/nostr_dev/models/notifications.py +18 -0
  32. nostr_dev-1.0.0/src/nostr_dev/models/relays.py +18 -0
  33. nostr_dev-1.0.0/src/nostr_dev/secrets.py +187 -0
  34. nostr_dev-1.0.0/src/nostr_dev/services/__init__.py +1 -0
  35. nostr_dev-1.0.0/src/nostr_dev/services/github.py +405 -0
  36. nostr_dev-1.0.0/src/nostr_dev/services/mapper.py +102 -0
  37. nostr_dev-1.0.0/src/nostr_dev/services/nostr.py +112 -0
  38. nostr_dev-1.0.0/src/nostr_dev/services/relay.py +295 -0
  39. nostr_dev-1.0.0/src/nostr_dev/services/scheduler.py +24 -0
  40. nostr_dev-1.0.0/src/nostr_dev/utils/__init__.py +1 -0
  41. nostr_dev-1.0.0/tests/integration/test_local_relay.py +54 -0
  42. nostr_dev-1.0.0/tests/unit/test_cli_hardening.py +79 -0
  43. nostr_dev-1.0.0/tests/unit/test_command_error_paths.py +45 -0
  44. nostr_dev-1.0.0/tests/unit/test_config.py +32 -0
  45. nostr_dev-1.0.0/tests/unit/test_events_commands.py +81 -0
  46. nostr_dev-1.0.0/tests/unit/test_exceptions.py +35 -0
  47. nostr_dev-1.0.0/tests/unit/test_github_commands.py +143 -0
  48. nostr_dev-1.0.0/tests/unit/test_github_opportunities.py +168 -0
  49. nostr_dev-1.0.0/tests/unit/test_github_service.py +106 -0
  50. nostr_dev-1.0.0/tests/unit/test_github_sync.py +185 -0
  51. nostr_dev-1.0.0/tests/unit/test_key_commands.py +197 -0
  52. nostr_dev-1.0.0/tests/unit/test_mapper.py +70 -0
  53. nostr_dev-1.0.0/tests/unit/test_migrations.py +131 -0
  54. nostr_dev-1.0.0/tests/unit/test_publish_commands.py +129 -0
  55. nostr_dev-1.0.0/tests/unit/test_relay_commands.py +67 -0
  56. nostr_dev-1.0.0/tests/unit/test_relay_publish_subscribe.py +103 -0
  57. nostr_dev-1.0.0/tests/unit/test_relay_service.py +101 -0
  58. nostr_dev-1.0.0/tests/unit/test_scaffold.py +11 -0
  59. nostr_dev-1.0.0/tests/unit/test_secrets.py +49 -0
  60. nostr_dev-1.0.0/uv.lock +1246 -0
@@ -0,0 +1,21 @@
1
+ # GitHub integration
2
+ # Optional: used by `nostr-dev github login` instead of prompting.
3
+ GITHUB_TOKEN=
4
+
5
+ # Optional: override the GitHub API base URL for tests or GitHub Enterprise.
6
+ NOSTR_DEV_GITHUB_API_BASE_URL=https://api.github.com
7
+
8
+ # Optional: recurring sync interval for `nostr-dev github daemon`.
9
+ NOSTR_DEV_SYNC_INTERVAL_SECONDS=900
10
+
11
+ # Optional: labels used by `nostr-dev github opportunities`.
12
+ NOSTR_DEV_OPPORTUNITY_LABELS=["good first issue","help wanted"]
13
+
14
+ # Optional: default relays for future config-driven workflows.
15
+ NOSTR_DEV_DEFAULT_RELAYS=[]
16
+
17
+ # Local database
18
+ NOSTR_DEV_DB_PATH=
19
+
20
+ # Secret storage
21
+ NOSTR_DEV_SECRET_BACKEND=
@@ -0,0 +1,75 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+ workflow_dispatch:
7
+
8
+ jobs:
9
+ unit:
10
+ runs-on: ubuntu-latest
11
+
12
+ steps:
13
+ - name: Check out repository
14
+ uses: actions/checkout@v5
15
+
16
+ - name: Install uv
17
+ uses: astral-sh/setup-uv@v6
18
+
19
+ - name: Set up Python
20
+ uses: actions/setup-python@v6
21
+ with:
22
+ python-version: "3.13"
23
+
24
+ - name: Install dependencies
25
+ run: uv sync
26
+
27
+ - name: Run ruff
28
+ run: uv run ruff check .
29
+
30
+ - name: Run mypy
31
+ run: uv run mypy src
32
+
33
+ - name: Run tests
34
+ run: uv run pytest tests/unit -v
35
+
36
+ integration:
37
+ if: github.event_name == 'workflow_dispatch'
38
+ runs-on: ubuntu-latest
39
+
40
+ steps:
41
+ - name: Check out repository
42
+ uses: actions/checkout@v5
43
+
44
+ - name: Install uv
45
+ uses: astral-sh/setup-uv@v6
46
+
47
+ - name: Set up Python
48
+ uses: actions/setup-python@v6
49
+ with:
50
+ python-version: "3.13"
51
+
52
+ - name: Install dependencies
53
+ run: uv sync
54
+
55
+ - name: Start local nostr-rs-relay
56
+ run: docker compose up -d
57
+
58
+ - name: Wait for local relay port
59
+ run: |
60
+ python - <<'PY'
61
+ import socket
62
+ import time
63
+
64
+ deadline = time.time() + 60
65
+ while time.time() < deadline:
66
+ with socket.socket() as sock:
67
+ sock.settimeout(1)
68
+ if sock.connect_ex(("127.0.0.1", 7777)) == 0:
69
+ raise SystemExit(0)
70
+ time.sleep(1)
71
+ raise SystemExit("nostr-rs-relay did not open port 7777 in time")
72
+ PY
73
+
74
+ - name: Run integration tests
75
+ run: uv run pytest tests/integration -m integration -v
@@ -0,0 +1,11 @@
1
+ .venv/
2
+ .mypy_cache/
3
+ .pytest_cache/
4
+ .ruff_cache/
5
+ __pycache__/
6
+ *.py[cod]
7
+ .coverage
8
+ htmlcov/
9
+
10
+ .env
11
+ .nostr-dev/
@@ -0,0 +1,20 @@
1
+ repos:
2
+ - repo: local
3
+ hooks:
4
+ - id: ruff-check
5
+ name: ruff check
6
+ entry: uv run ruff check --fix
7
+ language: system
8
+ types_or: [python, pyi]
9
+
10
+ - id: ruff-format
11
+ name: ruff format
12
+ entry: uv run ruff format
13
+ language: system
14
+ types_or: [python, pyi]
15
+
16
+ - id: mypy
17
+ name: mypy
18
+ entry: uv run mypy src
19
+ language: system
20
+ pass_filenames: false
@@ -0,0 +1,24 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented here.
4
+
5
+ ## 1.0.0 - MVP
6
+
7
+ - Added live GitHub opportunity discovery with configurable labels:
8
+ `nostr-dev github opportunities`.
9
+ - Added GitHub watch, sync, inbox, and daemon workflows for turning repository activity into
10
+ local events and Nostr notes.
11
+ - Added optional Docker-backed integration tests against `nostr-rs-relay`.
12
+ - Added complete MVP documentation, command reference, architecture diagram, non-goals, and
13
+ security notes.
14
+ - Verified local package entry point for isolated installs.
15
+
16
+ ## 0.1.0 - Core Nostr toolkit
17
+
18
+ - Added Python 3.13 project scaffolding, CI, linting, typing, and pre-commit setup.
19
+ - Added config, structured logging, SQLite/SQLModel database setup, and Alembic migrations.
20
+ - Added secure key management with keyring and encrypted-file secret storage.
21
+ - Added Nostr key commands: `keygen`, `import`, and `whoami`.
22
+ - Added relay management: `relay add`, `relay list`, `relay ping`, and `relay remove`.
23
+ - Added note publishing and event querying: `publish` and `events`.
24
+ - Added centralized CLI error handling with `--verbose` and `--debug`.
@@ -0,0 +1,254 @@
1
+ Metadata-Version: 2.4
2
+ Name: nostr-dev
3
+ Version: 1.0.0
4
+ Summary: Developer tooling for Nostr workflows.
5
+ Requires-Python: >=3.13
6
+ Requires-Dist: alembic
7
+ Requires-Dist: apscheduler
8
+ Requires-Dist: cryptography
9
+ Requires-Dist: httpx
10
+ Requires-Dist: keyring
11
+ Requires-Dist: nostr-sdk
12
+ Requires-Dist: pydantic-settings
13
+ Requires-Dist: rich
14
+ Requires-Dist: sqlmodel
15
+ Requires-Dist: structlog
16
+ Requires-Dist: tomli-w
17
+ Requires-Dist: typer
18
+ Description-Content-Type: text/markdown
19
+
20
+ # nostr-dev
21
+
22
+ Developer tooling for Nostr workflows: local key management, relay health checks, note
23
+ publishing, event querying, and GitHub-to-Nostr activity sync from the command line.
24
+
25
+ Status: MVP complete.
26
+
27
+ ## Install
28
+
29
+ From a local checkout with `uv`:
30
+
31
+ ```sh
32
+ uv sync
33
+ uv run nostr-dev --help
34
+ ```
35
+
36
+ Install as a CLI in an isolated environment:
37
+
38
+ ```sh
39
+ uv tool install .
40
+ nostr-dev --help
41
+ ```
42
+
43
+ Or with `pipx`:
44
+
45
+ ```sh
46
+ pipx install .
47
+ nostr-dev --help
48
+ ```
49
+
50
+ ## Architecture
51
+
52
+ ```text
53
+ +---------------------------+
54
+ | nostr-dev CLI |
55
+ | Typer + Rich command UX |
56
+ +-------------+-------------+
57
+ |
58
+ +----------------------+----------------------+
59
+ | |
60
+ +-------v--------+ +--------v-------+
61
+ | Local SQLite | | SecretStore |
62
+ | SQLModel | | keyring first |
63
+ | Alembic-ready | | encrypted file |
64
+ +-------+--------+ +--------+-------+
65
+ | |
66
+ | |
67
+ +-------v--------+ +--------------------+--------v-------+
68
+ | GitHub service | | Nostr services | RelayPool |
69
+ | httpx API | | key/event/filter | publish/query |
70
+ | rate-limit UX | | builders | nostr-sdk |
71
+ +-------+--------+ +---------+----------+--------+-------+
72
+ | | |
73
+ v v v
74
+ api.github.com NIP-01 events Nostr relays
75
+ ```
76
+
77
+ ## Command Reference
78
+
79
+ | Command | Purpose |
80
+ | --- | --- |
81
+ | `nostr-dev --version` | Print the installed version. |
82
+ | `nostr-dev --verbose` | Raise terminal log level to INFO. |
83
+ | `nostr-dev --debug` | Raise log level to DEBUG and show full tracebacks for unexpected errors. |
84
+ | `nostr-dev keygen [--show-secret] [--force]` | Generate a Nostr keypair, store the nsec in SecretStore, and store only npub metadata in SQLite. |
85
+ | `nostr-dev import [NSEC] [--force]` | Import an nsec from an argument or stdin. |
86
+ | `nostr-dev whoami` | Show the active npub. |
87
+ | `nostr-dev relay add URL` | Add a `wss://` relay. |
88
+ | `nostr-dev relay list` | List configured relays. |
89
+ | `nostr-dev relay ping [URL]` | Ping one relay or all active relays and record latency. |
90
+ | `nostr-dev relay remove URL [--yes]` | Remove a relay. |
91
+ | `nostr-dev publish "CONTENT"` | Publish a NIP-01 kind:1 note to all active relays. |
92
+ | `nostr-dev events [--author NPUB] [--kind K] [--limit N]` | Query active relays and print matching events. |
93
+ | `nostr-dev github login` | Validate and store a GitHub PAT. Reads `GITHUB_TOKEN` when set. |
94
+ | `nostr-dev github watch OWNER/REPO` | Validate and watch a GitHub repository. |
95
+ | `nostr-dev github list` | List watched repositories. |
96
+ | `nostr-dev github sync` | Fetch recent PRs, releases, and issues for watched repos, save new events, and publish them to Nostr when relays and a key are configured. |
97
+ | `nostr-dev github inbox [--all]` | Show locally stored GitHub events, unpublished by default. |
98
+ | `nostr-dev github opportunities [--label LABEL] [--repo OWNER/REPO]` | Live query for labeled open issues across watched repos. Does not publish or store notifications. |
99
+ | `nostr-dev github daemon` | Run scheduled GitHub sync using APScheduler and the configured sync interval. |
100
+
101
+ ## Demo Walkthrough
102
+
103
+ ### Day 1: Nostr toolkit
104
+
105
+ ```console
106
+ $ nostr-dev keygen
107
+ Generated Nostr key:
108
+ npub1vwpkjqgdl6k6autnc7wmfdmmuf9
109
+
110
+ $ nostr-dev relay add wss://relay.damus.io
111
+ Added relay: wss://relay.damus.io
112
+
113
+ $ nostr-dev relay add wss://relay.primal.net
114
+ Added relay: wss://relay.primal.net
115
+
116
+ $ nostr-dev relay ping
117
+ Relay Ping
118
+ +-------------------------------------------+
119
+ | Relay | Status | Latency |
120
+ |------------------------+--------+---------|
121
+ | wss://relay.damus.io | Online | 420 ms |
122
+ | wss://relay.primal.net | Online | 515 ms |
123
+ +-------------------------------------------+
124
+
125
+ $ nostr-dev publish "hello nostr, testing nostr-dev"
126
+ Event ID: a9368a716899e4c0de7cabdc359af05bde99bdf89034cfae263c9fbbc5aef4a8
127
+ 2/2 relays confirmed
128
+ Publish Results
129
+ +--------------------------------------------+
130
+ | Relay | Status | Reason |
131
+ |------------------------+----------+--------|
132
+ | wss://relay.damus.io | accepted | - |
133
+ | wss://relay.primal.net | accepted | - |
134
+ +--------------------------------------------+
135
+
136
+ $ nostr-dev events --author npub1vwpkjqgdl6k6autnc7wmfdmmuf9 --kind 1 --limit 5
137
+ Nostr Events
138
+ +-----------------------------------------------------------------------------+
139
+ | Author | Kind | Created | Content |
140
+ |-----------------------+------+-----------------------+----------------------|
141
+ | npub1vwpkjqgdl6k6aut… | 1 | 2026-07-11T22:02:38+… | hello nostr, testing |
142
+ | | | | nostr-dev |
143
+ +-----------------------------------------------------------------------------+
144
+ ```
145
+
146
+ ### Day 2: GitHub workflows
147
+
148
+ ```console
149
+ $ GITHUB_TOKEN=github_pat_redacted nostr-dev github login
150
+ Authenticated as octocat
151
+
152
+ $ nostr-dev github watch bitcoin/bitcoin
153
+ Watching bitcoin/bitcoin
154
+
155
+ $ nostr-dev github watch rust-nostr/nostr
156
+ Watching rust-nostr/nostr
157
+
158
+ $ nostr-dev github sync
159
+ 3 new events found, 3 published to Nostr
160
+
161
+ $ nostr-dev github inbox --all
162
+ GitHub Inbox
163
+ +-----------------------------------------------------------------------------+
164
+ | Repo | Type | Title | Published | Created |
165
+ |-----------------+--------------+---------------------------+-----------+---------|
166
+ | bitcoin/bitcoin | pull_request | PR #35120 Improve package | yes | 2026-… |
167
+ | rust-nostr/nostr| release | Release v1.0.0 | yes | 2026-… |
168
+ +-----------------------------------------------------------------------------+
169
+
170
+ $ nostr-dev github opportunities
171
+ GitHub Opportunities: bitcoin/bitcoin
172
+ +-----------------------------------------------------------------------------+
173
+ | Issue | Title | Label | URL |
174
+ |-------+-------------------------------+------------------+----------------------|
175
+ | #315 | Add focused relay smoke tests | good first issue | https://github.com/… |
176
+ +-----------------------------------------------------------------------------+
177
+
178
+ $ nostr-dev github opportunities --repo rust-nostr/nostr --label "help wanted"
179
+ GitHub Opportunities: rust-nostr/nostr
180
+ +-----------------------------------------------------------------------------+
181
+ | Issue | Title | Label | URL |
182
+ |-------+-------------------------------+-------------+----------------------|
183
+ | #42 | Improve subscription examples | help wanted | https://github.com/… |
184
+ +-----------------------------------------------------------------------------+
185
+ ```
186
+
187
+ Run recurring GitHub sync as a long-lived process:
188
+
189
+ ```sh
190
+ nostr-dev github daemon
191
+ ```
192
+
193
+ The daemon uses `NOSTR_DEV_SYNC_INTERVAL_SECONDS` and stores scheduler jobs in the same
194
+ SQLite database.
195
+
196
+ ## Configuration
197
+
198
+ `nostr-dev` reads non-secret config from environment variables prefixed with
199
+ `NOSTR_DEV_` and from `~/.nostr-dev/config.toml`. Environment variables win.
200
+
201
+ Useful variables:
202
+
203
+ | Variable | Purpose |
204
+ | --- | --- |
205
+ | `GITHUB_TOKEN` | Optional PAT source for `nostr-dev github login`. |
206
+ | `NOSTR_DEV_DB_PATH` | Override the SQLite database path. |
207
+ | `NOSTR_DEV_GITHUB_API_BASE_URL` | Override the GitHub API base URL. |
208
+ | `NOSTR_DEV_SYNC_INTERVAL_SECONDS` | Scheduler interval for `github daemon`. |
209
+ | `NOSTR_DEV_OPPORTUNITY_LABELS` | Default labels for `github opportunities`. |
210
+ | `NOSTR_DEV_SECRET_BACKEND` | Force `keyring` or `file` secret storage. |
211
+
212
+ ## Testing
213
+
214
+ Default tests are unit tests only:
215
+
216
+ ```sh
217
+ uv run pytest
218
+ uv run ruff check .
219
+ uv run mypy src
220
+ ```
221
+
222
+ Integration tests require Docker and a local nostr-rs-relay:
223
+
224
+ ```sh
225
+ docker compose up -d
226
+ uv run pytest -m integration
227
+ ```
228
+
229
+ The local relay is exposed at `ws://127.0.0.1:7777`. Override it with
230
+ `NOSTR_DEV_INTEGRATION_RELAY_URL`.
231
+
232
+ ## Security Notes
233
+
234
+ - Secret values are never logged intentionally.
235
+ - `nostr-dev keygen` and `nostr-dev import` store nsec values in SecretStore, not SQLite.
236
+ - `nostr-dev whoami`, `publish`, `events`, GitHub commands, and sync output never print nsec
237
+ values.
238
+ - `nostr-dev keygen --show-secret` is the only command path that prints a newly generated
239
+ nsec, and it asks for confirmation first.
240
+ - GitHub PATs are read from `GITHUB_TOKEN` or an interactive prompt, validated, then stored
241
+ in SecretStore.
242
+ - The OS keychain backend is preferred. The encrypted-file backend is a compatibility
243
+ fallback protected by a passphrase-derived Fernet key. It is best-effort local protection,
244
+ not HSM-grade storage.
245
+ - Relay publishing is public by design. Do not publish sensitive content to Nostr relays.
246
+
247
+ ## Non-Goals
248
+
249
+ - Multi-identity workflows. The MVP assumes a single active key.
250
+ - NIP-65 relay discovery.
251
+ - A web UI.
252
+ - HSM-grade secret storage for the encrypted-file backend.
253
+ - Running a production Nostr relay.
254
+ - Replacing GitHub notification systems; sync is intentionally small and explicit.
@@ -0,0 +1,235 @@
1
+ # nostr-dev
2
+
3
+ Developer tooling for Nostr workflows: local key management, relay health checks, note
4
+ publishing, event querying, and GitHub-to-Nostr activity sync from the command line.
5
+
6
+ Status: MVP complete.
7
+
8
+ ## Install
9
+
10
+ From a local checkout with `uv`:
11
+
12
+ ```sh
13
+ uv sync
14
+ uv run nostr-dev --help
15
+ ```
16
+
17
+ Install as a CLI in an isolated environment:
18
+
19
+ ```sh
20
+ uv tool install .
21
+ nostr-dev --help
22
+ ```
23
+
24
+ Or with `pipx`:
25
+
26
+ ```sh
27
+ pipx install .
28
+ nostr-dev --help
29
+ ```
30
+
31
+ ## Architecture
32
+
33
+ ```text
34
+ +---------------------------+
35
+ | nostr-dev CLI |
36
+ | Typer + Rich command UX |
37
+ +-------------+-------------+
38
+ |
39
+ +----------------------+----------------------+
40
+ | |
41
+ +-------v--------+ +--------v-------+
42
+ | Local SQLite | | SecretStore |
43
+ | SQLModel | | keyring first |
44
+ | Alembic-ready | | encrypted file |
45
+ +-------+--------+ +--------+-------+
46
+ | |
47
+ | |
48
+ +-------v--------+ +--------------------+--------v-------+
49
+ | GitHub service | | Nostr services | RelayPool |
50
+ | httpx API | | key/event/filter | publish/query |
51
+ | rate-limit UX | | builders | nostr-sdk |
52
+ +-------+--------+ +---------+----------+--------+-------+
53
+ | | |
54
+ v v v
55
+ api.github.com NIP-01 events Nostr relays
56
+ ```
57
+
58
+ ## Command Reference
59
+
60
+ | Command | Purpose |
61
+ | --- | --- |
62
+ | `nostr-dev --version` | Print the installed version. |
63
+ | `nostr-dev --verbose` | Raise terminal log level to INFO. |
64
+ | `nostr-dev --debug` | Raise log level to DEBUG and show full tracebacks for unexpected errors. |
65
+ | `nostr-dev keygen [--show-secret] [--force]` | Generate a Nostr keypair, store the nsec in SecretStore, and store only npub metadata in SQLite. |
66
+ | `nostr-dev import [NSEC] [--force]` | Import an nsec from an argument or stdin. |
67
+ | `nostr-dev whoami` | Show the active npub. |
68
+ | `nostr-dev relay add URL` | Add a `wss://` relay. |
69
+ | `nostr-dev relay list` | List configured relays. |
70
+ | `nostr-dev relay ping [URL]` | Ping one relay or all active relays and record latency. |
71
+ | `nostr-dev relay remove URL [--yes]` | Remove a relay. |
72
+ | `nostr-dev publish "CONTENT"` | Publish a NIP-01 kind:1 note to all active relays. |
73
+ | `nostr-dev events [--author NPUB] [--kind K] [--limit N]` | Query active relays and print matching events. |
74
+ | `nostr-dev github login` | Validate and store a GitHub PAT. Reads `GITHUB_TOKEN` when set. |
75
+ | `nostr-dev github watch OWNER/REPO` | Validate and watch a GitHub repository. |
76
+ | `nostr-dev github list` | List watched repositories. |
77
+ | `nostr-dev github sync` | Fetch recent PRs, releases, and issues for watched repos, save new events, and publish them to Nostr when relays and a key are configured. |
78
+ | `nostr-dev github inbox [--all]` | Show locally stored GitHub events, unpublished by default. |
79
+ | `nostr-dev github opportunities [--label LABEL] [--repo OWNER/REPO]` | Live query for labeled open issues across watched repos. Does not publish or store notifications. |
80
+ | `nostr-dev github daemon` | Run scheduled GitHub sync using APScheduler and the configured sync interval. |
81
+
82
+ ## Demo Walkthrough
83
+
84
+ ### Day 1: Nostr toolkit
85
+
86
+ ```console
87
+ $ nostr-dev keygen
88
+ Generated Nostr key:
89
+ npub1vwpkjqgdl6k6autnc7wmfdmmuf9
90
+
91
+ $ nostr-dev relay add wss://relay.damus.io
92
+ Added relay: wss://relay.damus.io
93
+
94
+ $ nostr-dev relay add wss://relay.primal.net
95
+ Added relay: wss://relay.primal.net
96
+
97
+ $ nostr-dev relay ping
98
+ Relay Ping
99
+ +-------------------------------------------+
100
+ | Relay | Status | Latency |
101
+ |------------------------+--------+---------|
102
+ | wss://relay.damus.io | Online | 420 ms |
103
+ | wss://relay.primal.net | Online | 515 ms |
104
+ +-------------------------------------------+
105
+
106
+ $ nostr-dev publish "hello nostr, testing nostr-dev"
107
+ Event ID: a9368a716899e4c0de7cabdc359af05bde99bdf89034cfae263c9fbbc5aef4a8
108
+ 2/2 relays confirmed
109
+ Publish Results
110
+ +--------------------------------------------+
111
+ | Relay | Status | Reason |
112
+ |------------------------+----------+--------|
113
+ | wss://relay.damus.io | accepted | - |
114
+ | wss://relay.primal.net | accepted | - |
115
+ +--------------------------------------------+
116
+
117
+ $ nostr-dev events --author npub1vwpkjqgdl6k6autnc7wmfdmmuf9 --kind 1 --limit 5
118
+ Nostr Events
119
+ +-----------------------------------------------------------------------------+
120
+ | Author | Kind | Created | Content |
121
+ |-----------------------+------+-----------------------+----------------------|
122
+ | npub1vwpkjqgdl6k6aut… | 1 | 2026-07-11T22:02:38+… | hello nostr, testing |
123
+ | | | | nostr-dev |
124
+ +-----------------------------------------------------------------------------+
125
+ ```
126
+
127
+ ### Day 2: GitHub workflows
128
+
129
+ ```console
130
+ $ GITHUB_TOKEN=github_pat_redacted nostr-dev github login
131
+ Authenticated as octocat
132
+
133
+ $ nostr-dev github watch bitcoin/bitcoin
134
+ Watching bitcoin/bitcoin
135
+
136
+ $ nostr-dev github watch rust-nostr/nostr
137
+ Watching rust-nostr/nostr
138
+
139
+ $ nostr-dev github sync
140
+ 3 new events found, 3 published to Nostr
141
+
142
+ $ nostr-dev github inbox --all
143
+ GitHub Inbox
144
+ +-----------------------------------------------------------------------------+
145
+ | Repo | Type | Title | Published | Created |
146
+ |-----------------+--------------+---------------------------+-----------+---------|
147
+ | bitcoin/bitcoin | pull_request | PR #35120 Improve package | yes | 2026-… |
148
+ | rust-nostr/nostr| release | Release v1.0.0 | yes | 2026-… |
149
+ +-----------------------------------------------------------------------------+
150
+
151
+ $ nostr-dev github opportunities
152
+ GitHub Opportunities: bitcoin/bitcoin
153
+ +-----------------------------------------------------------------------------+
154
+ | Issue | Title | Label | URL |
155
+ |-------+-------------------------------+------------------+----------------------|
156
+ | #315 | Add focused relay smoke tests | good first issue | https://github.com/… |
157
+ +-----------------------------------------------------------------------------+
158
+
159
+ $ nostr-dev github opportunities --repo rust-nostr/nostr --label "help wanted"
160
+ GitHub Opportunities: rust-nostr/nostr
161
+ +-----------------------------------------------------------------------------+
162
+ | Issue | Title | Label | URL |
163
+ |-------+-------------------------------+-------------+----------------------|
164
+ | #42 | Improve subscription examples | help wanted | https://github.com/… |
165
+ +-----------------------------------------------------------------------------+
166
+ ```
167
+
168
+ Run recurring GitHub sync as a long-lived process:
169
+
170
+ ```sh
171
+ nostr-dev github daemon
172
+ ```
173
+
174
+ The daemon uses `NOSTR_DEV_SYNC_INTERVAL_SECONDS` and stores scheduler jobs in the same
175
+ SQLite database.
176
+
177
+ ## Configuration
178
+
179
+ `nostr-dev` reads non-secret config from environment variables prefixed with
180
+ `NOSTR_DEV_` and from `~/.nostr-dev/config.toml`. Environment variables win.
181
+
182
+ Useful variables:
183
+
184
+ | Variable | Purpose |
185
+ | --- | --- |
186
+ | `GITHUB_TOKEN` | Optional PAT source for `nostr-dev github login`. |
187
+ | `NOSTR_DEV_DB_PATH` | Override the SQLite database path. |
188
+ | `NOSTR_DEV_GITHUB_API_BASE_URL` | Override the GitHub API base URL. |
189
+ | `NOSTR_DEV_SYNC_INTERVAL_SECONDS` | Scheduler interval for `github daemon`. |
190
+ | `NOSTR_DEV_OPPORTUNITY_LABELS` | Default labels for `github opportunities`. |
191
+ | `NOSTR_DEV_SECRET_BACKEND` | Force `keyring` or `file` secret storage. |
192
+
193
+ ## Testing
194
+
195
+ Default tests are unit tests only:
196
+
197
+ ```sh
198
+ uv run pytest
199
+ uv run ruff check .
200
+ uv run mypy src
201
+ ```
202
+
203
+ Integration tests require Docker and a local nostr-rs-relay:
204
+
205
+ ```sh
206
+ docker compose up -d
207
+ uv run pytest -m integration
208
+ ```
209
+
210
+ The local relay is exposed at `ws://127.0.0.1:7777`. Override it with
211
+ `NOSTR_DEV_INTEGRATION_RELAY_URL`.
212
+
213
+ ## Security Notes
214
+
215
+ - Secret values are never logged intentionally.
216
+ - `nostr-dev keygen` and `nostr-dev import` store nsec values in SecretStore, not SQLite.
217
+ - `nostr-dev whoami`, `publish`, `events`, GitHub commands, and sync output never print nsec
218
+ values.
219
+ - `nostr-dev keygen --show-secret` is the only command path that prints a newly generated
220
+ nsec, and it asks for confirmation first.
221
+ - GitHub PATs are read from `GITHUB_TOKEN` or an interactive prompt, validated, then stored
222
+ in SecretStore.
223
+ - The OS keychain backend is preferred. The encrypted-file backend is a compatibility
224
+ fallback protected by a passphrase-derived Fernet key. It is best-effort local protection,
225
+ not HSM-grade storage.
226
+ - Relay publishing is public by design. Do not publish sensitive content to Nostr relays.
227
+
228
+ ## Non-Goals
229
+
230
+ - Multi-identity workflows. The MVP assumes a single active key.
231
+ - NIP-65 relay discovery.
232
+ - A web UI.
233
+ - HSM-grade secret storage for the encrypted-file backend.
234
+ - Running a production Nostr relay.
235
+ - Replacing GitHub notification systems; sync is intentionally small and explicit.
@@ -0,0 +1,42 @@
1
+ [alembic]
2
+ script_location = src/nostr_dev/migrations
3
+ prepend_sys_path = .
4
+ path_separator = os
5
+
6
+ sqlalchemy.url = sqlite:///placeholder.db
7
+
8
+ [post_write_hooks]
9
+
10
+ [loggers]
11
+ keys = root,sqlalchemy,alembic
12
+
13
+ [handlers]
14
+ keys = console
15
+
16
+ [formatters]
17
+ keys = generic
18
+
19
+ [logger_root]
20
+ level = WARNING
21
+ handlers = console
22
+ qualname =
23
+
24
+ [logger_sqlalchemy]
25
+ level = WARNING
26
+ handlers =
27
+ qualname = sqlalchemy.engine
28
+
29
+ [logger_alembic]
30
+ level = INFO
31
+ handlers =
32
+ qualname = alembic
33
+
34
+ [handler_console]
35
+ class = StreamHandler
36
+ args = (sys.stderr,)
37
+ level = NOTSET
38
+ formatter = generic
39
+
40
+ [formatter_generic]
41
+ format = %(levelname)-5.5s [%(name)s] %(message)s
42
+ datefmt = %H:%M:%S
@@ -0,0 +1,10 @@
1
+ services:
2
+ nostr-rs-relay:
3
+ image: scsibug/nostr-rs-relay:latest
4
+ ports:
5
+ - "127.0.0.1:7777:8080"
6
+ volumes:
7
+ - nostr-rs-relay-data:/usr/src/app/db
8
+
9
+ volumes:
10
+ nostr-rs-relay-data: