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.
- nostr_dev-1.0.0/.env.example +21 -0
- nostr_dev-1.0.0/.github/workflows/ci.yml +75 -0
- nostr_dev-1.0.0/.gitignore +11 -0
- nostr_dev-1.0.0/.pre-commit-config.yaml +20 -0
- nostr_dev-1.0.0/CHANGELOG.md +24 -0
- nostr_dev-1.0.0/PKG-INFO +254 -0
- nostr_dev-1.0.0/README.md +235 -0
- nostr_dev-1.0.0/alembic.ini +42 -0
- nostr_dev-1.0.0/docker-compose.yml +10 -0
- nostr_dev-1.0.0/pyproject.toml +63 -0
- nostr_dev-1.0.0/src/nostr_dev/__init__.py +3 -0
- nostr_dev-1.0.0/src/nostr_dev/cli.py +62 -0
- nostr_dev-1.0.0/src/nostr_dev/commands/__init__.py +1 -0
- nostr_dev-1.0.0/src/nostr_dev/commands/common.py +80 -0
- nostr_dev-1.0.0/src/nostr_dev/commands/events.py +86 -0
- nostr_dev-1.0.0/src/nostr_dev/commands/github.py +483 -0
- nostr_dev-1.0.0/src/nostr_dev/commands/keys.py +152 -0
- nostr_dev-1.0.0/src/nostr_dev/commands/publish.py +84 -0
- nostr_dev-1.0.0/src/nostr_dev/commands/relay.py +184 -0
- nostr_dev-1.0.0/src/nostr_dev/config.py +102 -0
- nostr_dev-1.0.0/src/nostr_dev/db.py +53 -0
- nostr_dev-1.0.0/src/nostr_dev/exceptions.py +47 -0
- nostr_dev-1.0.0/src/nostr_dev/logging.py +83 -0
- nostr_dev-1.0.0/src/nostr_dev/migrations/env.py +64 -0
- nostr_dev-1.0.0/src/nostr_dev/migrations/script.py.mako +25 -0
- nostr_dev-1.0.0/src/nostr_dev/migrations/versions/.gitkeep +1 -0
- nostr_dev-1.0.0/src/nostr_dev/migrations/versions/41f8dd9a0e37_initial_schema.py +178 -0
- nostr_dev-1.0.0/src/nostr_dev/models/__init__.py +15 -0
- nostr_dev-1.0.0/src/nostr_dev/models/github.py +66 -0
- nostr_dev-1.0.0/src/nostr_dev/models/keys.py +16 -0
- nostr_dev-1.0.0/src/nostr_dev/models/notifications.py +18 -0
- nostr_dev-1.0.0/src/nostr_dev/models/relays.py +18 -0
- nostr_dev-1.0.0/src/nostr_dev/secrets.py +187 -0
- nostr_dev-1.0.0/src/nostr_dev/services/__init__.py +1 -0
- nostr_dev-1.0.0/src/nostr_dev/services/github.py +405 -0
- nostr_dev-1.0.0/src/nostr_dev/services/mapper.py +102 -0
- nostr_dev-1.0.0/src/nostr_dev/services/nostr.py +112 -0
- nostr_dev-1.0.0/src/nostr_dev/services/relay.py +295 -0
- nostr_dev-1.0.0/src/nostr_dev/services/scheduler.py +24 -0
- nostr_dev-1.0.0/src/nostr_dev/utils/__init__.py +1 -0
- nostr_dev-1.0.0/tests/integration/test_local_relay.py +54 -0
- nostr_dev-1.0.0/tests/unit/test_cli_hardening.py +79 -0
- nostr_dev-1.0.0/tests/unit/test_command_error_paths.py +45 -0
- nostr_dev-1.0.0/tests/unit/test_config.py +32 -0
- nostr_dev-1.0.0/tests/unit/test_events_commands.py +81 -0
- nostr_dev-1.0.0/tests/unit/test_exceptions.py +35 -0
- nostr_dev-1.0.0/tests/unit/test_github_commands.py +143 -0
- nostr_dev-1.0.0/tests/unit/test_github_opportunities.py +168 -0
- nostr_dev-1.0.0/tests/unit/test_github_service.py +106 -0
- nostr_dev-1.0.0/tests/unit/test_github_sync.py +185 -0
- nostr_dev-1.0.0/tests/unit/test_key_commands.py +197 -0
- nostr_dev-1.0.0/tests/unit/test_mapper.py +70 -0
- nostr_dev-1.0.0/tests/unit/test_migrations.py +131 -0
- nostr_dev-1.0.0/tests/unit/test_publish_commands.py +129 -0
- nostr_dev-1.0.0/tests/unit/test_relay_commands.py +67 -0
- nostr_dev-1.0.0/tests/unit/test_relay_publish_subscribe.py +103 -0
- nostr_dev-1.0.0/tests/unit/test_relay_service.py +101 -0
- nostr_dev-1.0.0/tests/unit/test_scaffold.py +11 -0
- nostr_dev-1.0.0/tests/unit/test_secrets.py +49 -0
- 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,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`.
|
nostr_dev-1.0.0/PKG-INFO
ADDED
|
@@ -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
|